summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/activate/activate.c82
-rw-r--r--src/analyze/analyze-verify.c80
-rw-r--r--src/analyze/analyze.c164
-rw-r--r--src/ask-password/ask-password.c91
-rw-r--r--src/backlight/backlight.c18
-rw-r--r--src/basic/af-list.c6
-rw-r--r--src/basic/alloc-util.c85
-rw-r--r--src/basic/alloc-util.h109
-rw-r--r--src/basic/arphrd-list.c4
-rw-r--r--src/basic/async.c8
-rw-r--r--src/basic/audit-util.c (renamed from src/basic/audit.c)11
-rw-r--r--src/basic/audit-util.h (renamed from src/basic/audit.h)2
-rw-r--r--src/basic/barrier.c3
-rw-r--r--src/basic/barrier.h2
-rw-r--r--src/basic/bitmap.c21
-rw-r--r--src/basic/bitmap.h4
-rw-r--r--src/basic/btrfs-util.c1025
-rw-r--r--src/basic/btrfs-util.h79
-rw-r--r--src/basic/bus-label.c6
-rw-r--r--src/basic/bus-label.h1
-rw-r--r--src/basic/c-rbtree.c679
-rw-r--r--src/basic/c-rbtree.h297
-rw-r--r--src/basic/calendarspec.c287
-rw-r--r--src/basic/calendarspec.h5
-rw-r--r--src/basic/cap-list.c7
-rw-r--r--src/basic/capability-util.c (renamed from src/basic/capability.c)90
-rw-r--r--src/basic/capability-util.h (renamed from src/basic/capability.h)18
-rw-r--r--src/basic/cgroup-util.c53
-rw-r--r--src/basic/cgroup-util.h10
-rw-r--r--src/basic/chattr-util.c109
-rw-r--r--src/basic/chattr-util.h28
-rw-r--r--src/basic/clock-util.c14
-rw-r--r--src/basic/clock-util.h1
-rw-r--r--src/basic/conf-files.c42
-rw-r--r--src/basic/copy.c25
-rw-r--r--src/basic/copy.h1
-rw-r--r--src/basic/cpu-set-util.c116
-rw-r--r--src/basic/cpu-set-util.h34
-rw-r--r--src/basic/def.h26
-rw-r--r--src/basic/device-nodes.c2
-rw-r--r--src/basic/device-nodes.h3
-rw-r--r--src/basic/dirent-util.c80
-rw-r--r--src/basic/dirent-util.h54
-rw-r--r--src/basic/env-util.c40
-rw-r--r--src/basic/env-util.h4
-rw-r--r--src/basic/errno-list.c9
-rw-r--r--src/basic/escape.c476
-rw-r--r--src/basic/escape.h53
-rw-r--r--src/basic/ether-addr-util.c46
-rw-r--r--src/basic/ether-addr-util.h4
-rw-r--r--src/basic/exit-status.c4
-rw-r--r--src/basic/exit-status.h2
-rw-r--r--src/basic/extract-word.c300
-rw-r--r--src/basic/extract-word.h37
-rw-r--r--src/basic/fd-util.c360
-rw-r--r--src/basic/fd-util.h78
-rw-r--r--src/basic/fdset.c22
-rw-r--r--src/basic/fdset.h6
-rw-r--r--src/basic/fileio-label.c6
-rw-r--r--src/basic/fileio-label.h1
-rw-r--r--src/basic/fileio.c486
-rw-r--r--src/basic/fileio.h37
-rw-r--r--src/basic/formats-util.h (renamed from src/shared/formats-util.h)0
-rw-r--r--src/basic/fs-util.c514
-rw-r--r--src/basic/fs-util.h77
-rw-r--r--src/basic/glob-util.c72
-rw-r--r--src/basic/glob-util.h38
-rw-r--r--src/basic/gunicode.c2
-rw-r--r--src/basic/gunicode.h2
-rw-r--r--src/basic/hash-funcs.c83
-rw-r--r--src/basic/hash-funcs.h67
-rw-r--r--src/basic/hashmap.c95
-rw-r--r--src/basic/hashmap.h44
-rw-r--r--src/basic/hexdecoct.c700
-rw-r--r--src/basic/hexdecoct.h55
-rw-r--r--src/basic/hostname-util.c14
-rw-r--r--src/basic/in-addr-util.c26
-rw-r--r--src/basic/in-addr-util.h8
-rw-r--r--src/basic/io-util.c266
-rw-r--r--src/basic/io-util.h79
-rw-r--r--src/basic/ioprio.h2
-rw-r--r--src/basic/json.c11
-rw-r--r--src/basic/json.h4
-rw-r--r--src/basic/label.c8
-rw-r--r--src/basic/locale-util.c105
-rw-r--r--src/basic/locale-util.h21
-rw-r--r--src/basic/lockfile-util.c14
-rw-r--r--src/basic/lockfile-util.h2
-rw-r--r--src/basic/log.c55
-rw-r--r--src/basic/log.h19
-rw-r--r--src/basic/login-util.c4
-rw-r--r--src/basic/login-util.h5
-rw-r--r--src/basic/macro.h190
-rw-r--r--src/basic/memfd-util.c19
-rw-r--r--src/basic/memfd-util.h5
-rw-r--r--src/basic/mempool.c5
-rw-r--r--src/basic/missing.h117
-rw-r--r--src/basic/mkdir-label.c3
-rw-r--r--src/basic/mkdir.c11
-rw-r--r--src/basic/mount-util.c533
-rw-r--r--src/basic/mount-util.h54
-rw-r--r--src/basic/nss-util.h (renamed from src/shared/nss-util.h)7
-rw-r--r--src/basic/ordered-set.h11
-rw-r--r--src/basic/parse-util.c536
-rw-r--r--src/basic/parse-util.h97
-rw-r--r--src/basic/path-util.c644
-rw-r--r--src/basic/path-util.h31
-rw-r--r--src/basic/prioq.c18
-rw-r--r--src/basic/prioq.h3
-rw-r--r--src/basic/proc-cmdline.c178
-rw-r--r--src/basic/proc-cmdline.h29
-rw-r--r--src/basic/process-util.c235
-rw-r--r--src/basic/process-util.h45
-rw-r--r--src/basic/random-util.c17
-rw-r--r--src/basic/random-util.h1
-rw-r--r--src/basic/ratelimit.c3
-rw-r--r--src/basic/ratelimit.h3
-rw-r--r--src/basic/replace-var.c7
-rw-r--r--src/basic/rlimit-util.c73
-rw-r--r--src/basic/rlimit-util.h (renamed from src/libsystemd/sd-event/event-util.h)16
-rw-r--r--src/basic/rm-rf.c24
-rw-r--r--src/basic/selinux-util.c139
-rw-r--r--src/basic/selinux-util.h10
-rw-r--r--src/basic/set.h1
-rw-r--r--src/basic/sigbus.c4
-rw-r--r--src/basic/signal-util.c18
-rw-r--r--src/basic/signal-util.h2
-rw-r--r--src/basic/siphash24.c258
-rw-r--r--src/basic/siphash24.h19
-rw-r--r--src/basic/smack-util.c17
-rw-r--r--src/basic/smack-util.h4
-rw-r--r--src/basic/socket-label.c23
-rw-r--r--src/basic/socket-util.c238
-rw-r--r--src/basic/socket-util.h26
-rw-r--r--src/basic/stat-util.c220
-rw-r--r--src/basic/stat-util.h71
-rw-r--r--src/basic/stdio-util.h78
-rw-r--r--src/basic/strbuf.c3
-rw-r--r--src/basic/strbuf.h2
-rw-r--r--src/basic/string-table.c (renamed from src/libsystemd/sd-resolve/resolve-util.h)23
-rw-r--r--src/basic/string-table.h107
-rw-r--r--src/basic/string-util.c856
-rw-r--r--src/basic/string-util.h191
-rw-r--r--src/basic/strv.c181
-rw-r--r--src/basic/strv.h19
-rw-r--r--src/basic/strxcpyx.c2
-rw-r--r--src/basic/strxcpyx.h2
-rw-r--r--src/basic/syslog-util.c116
-rw-r--r--src/basic/syslog-util.h34
-rw-r--r--src/basic/terminal-util.c305
-rw-r--r--src/basic/terminal-util.h29
-rw-r--r--src/basic/time-util.c290
-rw-r--r--src/basic/time-util.h12
-rw-r--r--src/basic/umask-util.h48
-rw-r--r--src/basic/unaligned.h47
-rw-r--r--src/basic/unit-name.c179
-rw-r--r--src/basic/unit-name.h222
-rw-r--r--src/basic/user-util.c482
-rw-r--r--src/basic/user-util.h67
-rw-r--r--src/basic/utf8.c7
-rw-r--r--src/basic/utf8.h2
-rw-r--r--src/basic/util.c6120
-rw-r--r--src/basic/util.h797
-rw-r--r--src/basic/verbs.c9
-rw-r--r--src/basic/virt.c61
-rw-r--r--src/basic/virt.h3
-rw-r--r--src/basic/web-util.c78
-rw-r--r--src/basic/web-util.h (renamed from src/core/dbus-snapshot.h)10
-rw-r--r--src/basic/xattr-util.c202
-rw-r--r--src/basic/xattr-util.h (renamed from src/core/snapshot.h)31
-rw-r--r--src/basic/xml.c5
-rw-r--r--src/binfmt/binfmt.c28
-rw-r--r--src/boot/bootctl.c43
-rw-r--r--src/boot/efi/boot.c6
-rw-r--r--src/boot/efi/console.c2
-rw-r--r--src/boot/efi/graphics.c2
-rw-r--r--src/boot/efi/linux.c2
-rw-r--r--src/boot/efi/pefile.c2
-rw-r--r--src/boot/efi/splash.c2
-rw-r--r--src/boot/efi/stub.c6
-rw-r--r--src/bootchart/bootchart.c41
-rw-r--r--src/bootchart/bootchart.h1
-rw-r--r--src/bootchart/store.c21
-rw-r--r--src/bootchart/store.h1
-rw-r--r--src/bootchart/svg.c37
-rw-r--r--src/bus-proxyd/bus-proxyd.c49
-rw-r--r--src/bus-proxyd/bus-xml-policy.c40
-rw-r--r--src/bus-proxyd/bus-xml-policy.h2
-rw-r--r--src/bus-proxyd/driver.c48
-rw-r--r--src/bus-proxyd/driver.h1
-rw-r--r--src/bus-proxyd/proxy.c74
-rw-r--r--src/bus-proxyd/proxy.h1
-rw-r--r--src/bus-proxyd/stdio-bridge.c26
-rw-r--r--src/bus-proxyd/synthesize.c19
-rw-r--r--src/bus-proxyd/synthesize.h1
-rw-r--r--src/bus-proxyd/test-bus-xml-policy.c11
-rw-r--r--src/cgls/cgls.c40
-rw-r--r--src/cgroups-agent/cgroups-agent.c7
-rw-r--r--src/cgtop/cgtop.c46
-rw-r--r--src/core/.gitignore1
-rw-r--r--src/core/audit-fd.c4
-rw-r--r--src/core/automount.c123
-rw-r--r--src/core/automount.h12
-rw-r--r--src/core/bus-endpoint.c5
-rw-r--r--src/core/bus-endpoint.h2
-rw-r--r--src/core/bus-policy.c7
-rw-r--r--src/core/bus-policy.h2
-rw-r--r--src/core/busname.c63
-rw-r--r--src/core/busname.h17
-rw-r--r--src/core/cgroup.c36
-rw-r--r--src/core/cgroup.h2
-rw-r--r--src/core/dbus-automount.c3
-rw-r--r--src/core/dbus-busname.c5
-rw-r--r--src/core/dbus-cgroup.c17
-rw-r--r--src/core/dbus-cgroup.h1
-rw-r--r--src/core/dbus-device.c4
-rw-r--r--src/core/dbus-execute.c624
-rw-r--r--src/core/dbus-execute.h1
-rw-r--r--src/core/dbus-job.c13
-rw-r--r--src/core/dbus-job.h1
-rw-r--r--src/core/dbus-kill.c5
-rw-r--r--src/core/dbus-kill.h3
-rw-r--r--src/core/dbus-manager.c268
-rw-r--r--src/core/dbus-mount.c9
-rw-r--r--src/core/dbus-mount.h1
-rw-r--r--src/core/dbus-path.c7
-rw-r--r--src/core/dbus-scope.c17
-rw-r--r--src/core/dbus-scope.h1
-rw-r--r--src/core/dbus-service.c55
-rw-r--r--src/core/dbus-service.h1
-rw-r--r--src/core/dbus-slice.c4
-rw-r--r--src/core/dbus-slice.h1
-rw-r--r--src/core/dbus-snapshot.c55
-rw-r--r--src/core/dbus-socket.c32
-rw-r--r--src/core/dbus-socket.h1
-rw-r--r--src/core/dbus-swap.c9
-rw-r--r--src/core/dbus-swap.h1
-rw-r--r--src/core/dbus-target.c2
-rw-r--r--src/core/dbus-target.h1
-rw-r--r--src/core/dbus-timer.c41
-rw-r--r--src/core/dbus-timer.h1
-rw-r--r--src/core/dbus-unit.c82
-rw-r--r--src/core/dbus-unit.h3
-rw-r--r--src/core/dbus.c123
-rw-r--r--src/core/dbus.h2
-rw-r--r--src/core/device.c61
-rw-r--r--src/core/device.h13
-rw-r--r--src/core/execute.c466
-rw-r--r--src/core/execute.h44
-rw-r--r--src/core/failure-action.c27
-rw-r--r--src/core/hostname-setup.c17
-rw-r--r--src/core/ima-setup.c6
-rw-r--r--src/core/job.c114
-rw-r--r--src/core/job.h8
-rw-r--r--src/core/kill.c5
-rw-r--r--src/core/killall.c14
-rw-r--r--src/core/kmod-setup.c6
-rw-r--r--src/core/load-dropin.c7
-rw-r--r--src/core/load-dropin.h2
-rw-r--r--src/core/load-fragment-gperf.gperf.m439
-rw-r--r--src/core/load-fragment.c1188
-rw-r--r--src/core/load-fragment.h10
-rw-r--r--src/core/locale-setup.c11
-rw-r--r--src/core/loopback-setup.c11
-rw-r--r--src/core/machine-id-setup.c49
-rw-r--r--src/core/machine-id-setup.h2
-rw-r--r--src/core/macros.systemd.in14
-rw-r--r--src/core/main.c692
-rw-r--r--src/core/manager.c377
-rw-r--r--src/core/manager.h21
-rw-r--r--src/core/mount-setup.c42
-rw-r--r--src/core/mount.c177
-rw-r--r--src/core/mount.h23
-rw-r--r--src/core/namespace.c74
-rw-r--r--src/core/path.c58
-rw-r--r--src/core/path.h12
-rw-r--r--src/core/scope.c64
-rw-r--r--src/core/scope.h14
-rw-r--r--src/core/selinux-access.c101
-rw-r--r--src/core/selinux-access.h3
-rw-r--r--src/core/selinux-setup.c15
-rw-r--r--src/core/service.c572
-rw-r--r--src/core/service.h34
-rw-r--r--src/core/show-status.c84
-rw-r--r--src/core/show-status.h7
-rw-r--r--src/core/shutdown.c34
-rw-r--r--src/core/slice.c44
-rw-r--r--src/core/slice.h11
-rw-r--r--src/core/smack-setup.c124
-rw-r--r--src/core/snapshot.c306
-rw-r--r--src/core/socket.c406
-rw-r--r--src/core/socket.h36
-rw-r--r--src/core/swap.c110
-rw-r--r--src/core/swap.h21
-rw-r--r--src/core/system.conf7
-rw-r--r--src/core/target.c17
-rw-r--r--src/core/target.h11
-rw-r--r--src/core/timer.c134
-rw-r--r--src/core/timer.h15
-rw-r--r--src/core/transaction.c87
-rw-r--r--src/core/transaction.h7
-rw-r--r--src/core/triggers.systemd.in64
-rw-r--r--src/core/umount.c41
-rw-r--r--src/core/unit-printf.c186
-rw-r--r--src/core/unit.c398
-rw-r--r--src/core/unit.h65
-rw-r--r--src/cryptsetup/cryptsetup-generator.c8
-rw-r--r--src/cryptsetup/cryptsetup.c80
-rw-r--r--src/dbus1-generator/dbus1-generator.c20
-rw-r--r--src/debug-generator/debug-generator.c36
-rw-r--r--src/delta/delta.c28
-rw-r--r--src/detect-virt/detect-virt.c42
-rw-r--r--src/escape/escape.c11
-rw-r--r--src/firstboot/firstboot.c81
-rw-r--r--src/fsck/fsck.c52
-rw-r--r--src/fstab-generator/fstab-generator.c38
-rw-r--r--src/getty-generator/getty-generator.c17
-rw-r--r--src/gpt-auto-generator/gpt-auto-generator.c49
-rw-r--r--src/hibernate-resume/hibernate-resume-generator.c10
-rw-r--r--src/hibernate-resume/hibernate-resume.c5
-rw-r--r--src/hostname/hostnamectl.c29
-rw-r--r--src/hostname/hostnamed.c37
-rw-r--r--src/hwdb/hwdb.c27
-rw-r--r--src/import/aufs-util.c7
-rw-r--r--src/import/curl-util.c21
-rw-r--r--src/import/curl-util.h5
-rw-r--r--src/import/export-raw.c15
-rw-r--r--src/import/export-raw.h3
-rw-r--r--src/import/export-tar.c17
-rw-r--r--src/import/export-tar.h3
-rw-r--r--src/import/export.c25
-rw-r--r--src/import/import-common.c12
-rw-r--r--src/import/import-compress.c3
-rw-r--r--src/import/import-compress.h5
-rw-r--r--src/import/import-raw.c30
-rw-r--r--src/import/import-raw.h3
-rw-r--r--src/import/import-tar.c29
-rw-r--r--src/import/import-tar.h3
-rw-r--r--src/import/import.c25
-rw-r--r--src/import/importd.c157
-rw-r--r--src/import/org.freedesktop.import1.conf4
-rw-r--r--src/import/pull-common.c71
-rw-r--r--src/import/pull-common.h2
-rw-r--r--src/import/pull-dkr.c1331
-rw-r--r--src/import/pull-dkr.h37
-rw-r--r--src/import/pull-job.c9
-rw-r--r--src/import/pull-job.h2
-rw-r--r--src/import/pull-raw.c49
-rw-r--r--src/import/pull-raw.h3
-rw-r--r--src/import/pull-tar.c39
-rw-r--r--src/import/pull-tar.h3
-rw-r--r--src/import/pull.c151
-rw-r--r--src/import/qcow2-util.c7
-rw-r--r--src/import/test-qcow2.c4
-rw-r--r--src/initctl/initctl.c29
-rw-r--r--src/journal-remote/journal-gatewayd.c62
-rw-r--r--src/journal-remote/journal-remote-parse.c4
-rw-r--r--src/journal-remote/journal-remote-parse.h1
-rw-r--r--src/journal-remote/journal-remote-write.c1
-rw-r--r--src/journal-remote/journal-remote.c76
-rw-r--r--src/journal-remote/journal-remote.conf.in1
-rw-r--r--src/journal-remote/journal-remote.h4
-rw-r--r--src/journal-remote/journal-upload-journal.c34
-rw-r--r--src/journal-remote/journal-upload.c28
-rw-r--r--src/journal-remote/journal-upload.h2
-rwxr-xr-xsrc/journal-remote/log-generator.py10
-rw-r--r--src/journal-remote/microhttpd-util.c14
-rw-r--r--src/journal-remote/microhttpd-util.h11
-rw-r--r--src/journal/audit-type.c5
-rw-r--r--src/journal/cat.c45
-rw-r--r--src/journal/catalog.c88
-rw-r--r--src/journal/catalog.h1
-rw-r--r--src/journal/compress.c275
-rw-r--r--src/journal/compress.h13
-rw-r--r--src/journal/coredump-vacuum.c27
-rw-r--r--src/journal/coredump.c55
-rw-r--r--src/journal/coredumpctl.c62
-rw-r--r--src/journal/fsprg.h3
-rw-r--r--src/journal/journal-authenticate.c6
-rw-r--r--src/journal/journal-def.h5
-rw-r--r--src/journal/journal-file.c135
-rw-r--r--src/journal/journal-file.h24
-rw-r--r--src/journal/journal-internal.h15
-rw-r--r--src/journal/journal-qrcode.c7
-rw-r--r--src/journal/journal-qrcode.h3
-rw-r--r--src/journal/journal-send.c53
-rw-r--r--src/journal/journal-vacuum.c136
-rw-r--r--src/journal/journal-vacuum.h6
-rw-r--r--src/journal/journal-verify.c23
-rw-r--r--src/journal/journalctl.c388
-rw-r--r--src/journal/journald-audit.c9
-rw-r--r--src/journal/journald-audit.h2
-rw-r--r--src/journal/journald-console.c13
-rw-r--r--src/journal/journald-gperf.gperf2
-rw-r--r--src/journal/journald-kmsg.c27
-rw-r--r--src/journal/journald-native.c60
-rw-r--r--src/journal/journald-rate-limit.c22
-rw-r--r--src/journal/journald-server.c717
-rw-r--r--src/journal/journald-server.h39
-rw-r--r--src/journal/journald-stream.c116
-rw-r--r--src/journal/journald-stream.h6
-rw-r--r--src/journal/journald-syslog.c33
-rw-r--r--src/journal/journald-wall.c6
-rw-r--r--src/journal/journald.c21
-rw-r--r--src/journal/journald.conf2
-rw-r--r--src/journal/lookup3.c4
-rw-r--r--src/journal/mmap-cache.c23
-rw-r--r--src/journal/sd-journal.c358
-rw-r--r--src/journal/stacktrace.c7
-rw-r--r--src/journal/test-catalog.c15
-rw-r--r--src/journal/test-compress-benchmark.c117
-rw-r--r--src/journal/test-compress.c135
-rw-r--r--src/journal/test-journal-enum.c7
-rw-r--r--src/journal/test-journal-flush.c5
-rw-r--r--src/journal/test-journal-init.c5
-rw-r--r--src/journal/test-journal-interleaving.c11
-rw-r--r--src/journal/test-journal-match.c8
-rw-r--r--src/journal/test-journal-send.c92
-rw-r--r--src/journal/test-journal-stream.c13
-rw-r--r--src/journal/test-journal-syslog.c2
-rw-r--r--src/journal/test-journal-verify.c9
-rw-r--r--src/journal/test-journal.c10
-rw-r--r--src/journal/test-mmap-cache.c6
-rw-r--r--src/libsystemd-network/arp-util.c3
-rw-r--r--src/libsystemd-network/arp-util.h2
-rw-r--r--src/libsystemd-network/dhcp-identifier.c27
-rw-r--r--src/libsystemd-network/dhcp-identifier.h2
-rw-r--r--src/libsystemd-network/dhcp-internal.h16
-rw-r--r--src/libsystemd-network/dhcp-lease-internal.h10
-rw-r--r--src/libsystemd-network/dhcp-network.c12
-rw-r--r--src/libsystemd-network/dhcp-option.c125
-rw-r--r--src/libsystemd-network/dhcp-packet.c7
-rw-r--r--src/libsystemd-network/dhcp-protocol.h45
-rw-r--r--src/libsystemd-network/dhcp-server-internal.h12
-rw-r--r--src/libsystemd-network/dhcp6-internal.h6
-rw-r--r--src/libsystemd-network/dhcp6-lease-internal.h4
-rw-r--r--src/libsystemd-network/dhcp6-network.c135
-rw-r--r--src/libsystemd-network/dhcp6-option.c46
-rw-r--r--src/libsystemd-network/dhcp6-protocol.h35
-rw-r--r--src/libsystemd-network/icmp6-util.c127
-rw-r--r--src/libsystemd-network/icmp6-util.h27
-rw-r--r--src/libsystemd-network/lldp-internal.c253
-rw-r--r--src/libsystemd-network/lldp-internal.h13
-rw-r--r--src/libsystemd-network/lldp-network.c34
-rw-r--r--src/libsystemd-network/lldp-network.h1
-rw-r--r--src/libsystemd-network/lldp-port.c12
-rw-r--r--src/libsystemd-network/lldp-tlv.c339
-rw-r--r--src/libsystemd-network/lldp-tlv.h29
-rw-r--r--src/libsystemd-network/lldp.h13
-rw-r--r--src/libsystemd-network/network-internal.c56
-rw-r--r--src/libsystemd-network/network-internal.h8
-rw-r--r--src/libsystemd-network/sd-dhcp-client.c184
-rw-r--r--src/libsystemd-network/sd-dhcp-lease.c139
-rw-r--r--src/libsystemd-network/sd-dhcp-server.c84
-rw-r--r--src/libsystemd-network/sd-dhcp6-client.c145
-rw-r--r--src/libsystemd-network/sd-dhcp6-lease.c8
-rw-r--r--src/libsystemd-network/sd-icmp6-nd.c722
-rw-r--r--src/libsystemd-network/sd-ipv4acd.c20
-rw-r--r--src/libsystemd-network/sd-ipv4ll.c73
-rw-r--r--src/libsystemd-network/sd-lldp.c164
-rw-r--r--src/libsystemd-network/sd-ndisc.c718
-rw-r--r--src/libsystemd-network/sd-pppoe.c810
-rw-r--r--src/libsystemd-network/test-acd.c15
-rw-r--r--src/libsystemd-network/test-dhcp-client.c43
-rw-r--r--src/libsystemd-network/test-dhcp-option.c78
-rw-r--r--src/libsystemd-network/test-dhcp-server.c40
-rw-r--r--src/libsystemd-network/test-dhcp6-client.c90
-rw-r--r--src/libsystemd-network/test-icmp6-rs.c357
-rw-r--r--src/libsystemd-network/test-ipv4ll-manual.c21
-rw-r--r--src/libsystemd-network/test-ipv4ll.c25
-rw-r--r--src/libsystemd-network/test-lldp.c259
-rw-r--r--src/libsystemd-network/test-ndisc-rs.c170
-rw-r--r--src/libsystemd-network/test-pppoe.c176
-rw-r--r--src/libsystemd/libsystemd.sym3
-rw-r--r--src/libsystemd/sd-bus/bus-bloom.c10
-rw-r--r--src/libsystemd/sd-bus/bus-bloom.h1
-rw-r--r--src/libsystemd/sd-bus/bus-common-errors.c12
-rw-r--r--src/libsystemd/sd-bus/bus-common-errors.h7
-rw-r--r--src/libsystemd/sd-bus/bus-container.c20
-rw-r--r--src/libsystemd/sd-bus/bus-control.c41
-rw-r--r--src/libsystemd/sd-bus/bus-control.h1
-rw-r--r--src/libsystemd/sd-bus/bus-convenience.c29
-rw-r--r--src/libsystemd/sd-bus/bus-creds.c34
-rw-r--r--src/libsystemd/sd-bus/bus-dump.c21
-rw-r--r--src/libsystemd/sd-bus/bus-dump.h2
-rw-r--r--src/libsystemd/sd-bus/bus-error.c69
-rw-r--r--src/libsystemd/sd-bus/bus-error.h1
-rw-r--r--src/libsystemd/sd-bus/bus-gvariant.c2
-rw-r--r--src/libsystemd/sd-bus/bus-internal.c5
-rw-r--r--src/libsystemd/sd-bus/bus-internal.h20
-rw-r--r--src/libsystemd/sd-bus/bus-introspect.c9
-rw-r--r--src/libsystemd/sd-bus/bus-introspect.h1
-rw-r--r--src/libsystemd/sd-bus/bus-kernel.c79
-rw-r--r--src/libsystemd/sd-bus/bus-match.c9
-rw-r--r--src/libsystemd/sd-bus/bus-match.h4
-rw-r--r--src/libsystemd/sd-bus/bus-message.c34
-rw-r--r--src/libsystemd/sd-bus/bus-message.h7
-rw-r--r--src/libsystemd/sd-bus/bus-objects.c73
-rw-r--r--src/libsystemd/sd-bus/bus-slot.c7
-rw-r--r--src/libsystemd/sd-bus/bus-slot.h1
-rw-r--r--src/libsystemd/sd-bus/bus-socket.c33
-rw-r--r--src/libsystemd/sd-bus/bus-track.c12
-rw-r--r--src/libsystemd/sd-bus/bus-type.h3
-rw-r--r--src/libsystemd/sd-bus/busctl-introspect.c8
-rw-r--r--src/libsystemd/sd-bus/busctl.c73
-rw-r--r--src/libsystemd/sd-bus/sd-bus.c240
-rw-r--r--src/libsystemd/sd-bus/test-bus-benchmark.c20
-rw-r--r--src/libsystemd/sd-bus/test-bus-chat.c36
-rw-r--r--src/libsystemd/sd-bus/test-bus-cleanup.c19
-rw-r--r--src/libsystemd/sd-bus/test-bus-creds.c5
-rw-r--r--src/libsystemd/sd-bus/test-bus-error.c36
-rw-r--r--src/libsystemd/sd-bus/test-bus-gvariant.c14
-rw-r--r--src/libsystemd/sd-bus/test-bus-introspect.c2
-rw-r--r--src/libsystemd/sd-bus/test-bus-kernel-bloom.c10
-rw-r--r--src/libsystemd/sd-bus/test-bus-kernel.c14
-rw-r--r--src/libsystemd/sd-bus/test-bus-marshal.c52
-rw-r--r--src/libsystemd/sd-bus/test-bus-match.c11
-rw-r--r--src/libsystemd/sd-bus/test-bus-objects.c21
-rw-r--r--src/libsystemd/sd-bus/test-bus-proxy.c11
-rw-r--r--src/libsystemd/sd-bus/test-bus-server.c16
-rw-r--r--src/libsystemd/sd-bus/test-bus-signature.c6
-rw-r--r--src/libsystemd/sd-bus/test-bus-zero-copy.c15
-rw-r--r--src/libsystemd/sd-daemon/sd-daemon.c135
l---------src/libsystemd/sd-device/Makefile (renamed from src/machine-id-commit/Makefile)0
-rw-r--r--src/libsystemd/sd-device/device-enumerator.c29
-rw-r--r--src/libsystemd/sd-device/device-private.c50
-rw-r--r--src/libsystemd/sd-device/device-private.h6
-rw-r--r--src/libsystemd/sd-device/device-util.h6
-rw-r--r--src/libsystemd/sd-device/sd-device.c81
-rw-r--r--src/libsystemd/sd-event/sd-event.c192
-rw-r--r--src/libsystemd/sd-event/test-event.c50
-rw-r--r--src/libsystemd/sd-hwdb/hwdb-internal.h1
-rw-r--r--src/libsystemd/sd-hwdb/hwdb-util.h5
-rw-r--r--src/libsystemd/sd-hwdb/sd-hwdb.c16
-rw-r--r--src/libsystemd/sd-id128/sd-id128.c8
-rw-r--r--src/libsystemd/sd-login/sd-login.c37
-rw-r--r--src/libsystemd/sd-login/test-login.c9
-rw-r--r--src/libsystemd/sd-netlink/local-addresses.c14
-rw-r--r--src/libsystemd/sd-netlink/local-addresses.h1
-rw-r--r--src/libsystemd/sd-netlink/netlink-internal.h17
-rw-r--r--src/libsystemd/sd-netlink/netlink-message.c24
-rw-r--r--src/libsystemd/sd-netlink/netlink-socket.c172
-rw-r--r--src/libsystemd/sd-netlink/netlink-types.c71
-rw-r--r--src/libsystemd/sd-netlink/netlink-types.h2
-rw-r--r--src/libsystemd/sd-netlink/netlink-util.c7
-rw-r--r--src/libsystemd/sd-netlink/netlink-util.h8
-rw-r--r--src/libsystemd/sd-netlink/rtnl-message.c101
-rw-r--r--src/libsystemd/sd-netlink/sd-netlink.c94
-rw-r--r--src/libsystemd/sd-netlink/test-local-addresses.c3
-rw-r--r--src/libsystemd/sd-netlink/test-netlink.c42
-rw-r--r--src/libsystemd/sd-network/network-util.c4
-rw-r--r--src/libsystemd/sd-network/network-util.h3
-rw-r--r--src/libsystemd/sd-network/sd-network.c175
-rw-r--r--src/libsystemd/sd-path/sd-path.c12
-rw-r--r--src/libsystemd/sd-resolve/sd-resolve.c35
-rw-r--r--src/libsystemd/sd-resolve/test-resolve.c26
-rw-r--r--src/libsystemd/sd-utf8/sd-utf8.c5
-rw-r--r--src/libudev/libudev-device-internal.h3
-rw-r--r--src/libudev/libudev-device-private.c12
-rw-r--r--src/libudev/libudev-device.c30
-rw-r--r--src/libudev/libudev-enumerate.c19
-rw-r--r--src/libudev/libudev-hwdb.c6
-rw-r--r--src/libudev/libudev-list.c5
-rw-r--r--src/libudev/libudev-monitor.c28
-rw-r--r--src/libudev/libudev-private.h7
-rw-r--r--src/libudev/libudev-queue.c9
-rw-r--r--src/libudev/libudev-util.c60
-rw-r--r--src/libudev/libudev.c10
-rw-r--r--src/locale/localectl.c39
-rw-r--r--src/locale/localed.c39
-rw-r--r--src/login/inhibit.c27
-rw-r--r--src/login/loginctl.c89
-rw-r--r--src/login/logind-acl.c11
-rw-r--r--src/login/logind-acl.h5
-rw-r--r--src/login/logind-action.c16
-rw-r--r--src/login/logind-action.h1
-rw-r--r--src/login/logind-button.c8
-rw-r--r--src/login/logind-core.c26
-rw-r--r--src/login/logind-dbus.c309
-rw-r--r--src/login/logind-device.c3
-rw-r--r--src/login/logind-gperf.gperf1
-rw-r--r--src/login/logind-inhibit.c13
-rw-r--r--src/login/logind-seat-dbus.c14
-rw-r--r--src/login/logind-seat.c17
-rw-r--r--src/login/logind-session-dbus.c23
-rw-r--r--src/login/logind-session-device.c11
-rw-r--r--src/login/logind-session.c72
-rw-r--r--src/login/logind-session.h2
-rw-r--r--src/login/logind-user-dbus.c12
-rw-r--r--src/login/logind-user.c304
-rw-r--r--src/login/logind-user.h12
-rw-r--r--src/login/logind-utmp.c21
-rw-r--r--src/login/logind.c42
-rw-r--r--src/login/logind.conf1
-rw-r--r--src/login/logind.h15
-rw-r--r--src/login/pam_systemd.c67
-rw-r--r--src/login/sysfs-show.c14
-rw-r--r--src/login/test-inhibit.c16
-rw-r--r--src/login/test-login-shared.c2
-rw-r--r--src/login/test-login-tables.c1
-rw-r--r--src/machine-id-commit/machine-id-commit.c107
-rw-r--r--src/machine-id-setup/machine-id-setup-main.c46
-rw-r--r--src/machine/image-dbus.c6
-rw-r--r--src/machine/machine-dbus.c70
-rw-r--r--src/machine/machine-dbus.h2
-rw-r--r--src/machine/machine.c76
-rw-r--r--src/machine/machine.h1
-rw-r--r--src/machine/machinectl.c270
-rw-r--r--src/machine/machined-dbus.c68
-rw-r--r--src/machine/machined.c17
-rw-r--r--src/machine/machined.h11
-rw-r--r--src/machine/test-machine-tables.c1
-rw-r--r--src/modules-load/modules-load.c27
-rw-r--r--src/network/networkctl.c83
-rw-r--r--src/network/networkd-address-pool.c8
-rw-r--r--src/network/networkd-address-pool.h1
-rw-r--r--src/network/networkd-address.c475
-rw-r--r--src/network/networkd-address.h23
-rw-r--r--src/network/networkd-dhcp4.c108
-rw-r--r--src/network/networkd-dhcp6.c260
-rw-r--r--src/network/networkd-fdb.c12
-rw-r--r--src/network/networkd-fdb.h2
-rw-r--r--src/network/networkd-ipv4ll.c52
-rw-r--r--src/network/networkd-link-bus.c22
-rw-r--r--src/network/networkd-link.c1036
-rw-r--r--src/network/networkd-link.h39
-rw-r--r--src/network/networkd-manager-bus.c2
-rw-r--r--src/network/networkd-manager.c825
-rw-r--r--src/network/networkd-ndisc.c251
-rw-r--r--src/network/networkd-netdev-bond.c48
-rw-r--r--src/network/networkd-netdev-bridge.c89
-rw-r--r--src/network/networkd-netdev-bridge.h4
-rw-r--r--src/network/networkd-netdev-gperf.gperf4
-rw-r--r--src/network/networkd-netdev-ipvlan.c15
-rw-r--r--src/network/networkd-netdev-macvlan.c3
-rw-r--r--src/network/networkd-netdev-tunnel.c66
-rw-r--r--src/network/networkd-netdev-tuntap.c5
-rw-r--r--src/network/networkd-netdev-veth.c20
-rw-r--r--src/network/networkd-netdev-vlan.c14
-rw-r--r--src/network/networkd-netdev-vxlan.c30
-rw-r--r--src/network/networkd-netdev-vxlan.h4
-rw-r--r--src/network/networkd-netdev.c27
-rw-r--r--src/network/networkd-netdev.h17
-rw-r--r--src/network/networkd-network-bus.c5
-rw-r--r--src/network/networkd-network-gperf.gperf8
-rw-r--r--src/network/networkd-network.c130
-rw-r--r--src/network/networkd-network.h13
-rw-r--r--src/network/networkd-route.c445
-rw-r--r--src/network/networkd-route.h34
-rw-r--r--src/network/networkd-util.c69
-rw-r--r--src/network/networkd-util.h12
-rw-r--r--src/network/networkd-wait-online-link.c3
-rw-r--r--src/network/networkd-wait-online-manager.c7
-rw-r--r--src/network/networkd-wait-online.c10
-rw-r--r--src/network/networkd.c12
-rw-r--r--src/network/networkd.h17
-rw-r--r--src/network/test-network-tables.c9
-rw-r--r--src/network/test-network.c18
-rw-r--r--src/notify/notify.c8
-rw-r--r--src/nspawn/nspawn-cgroup.c13
-rw-r--r--src/nspawn/nspawn-cgroup.h2
-rw-r--r--src/nspawn/nspawn-expose-ports.c16
-rw-r--r--src/nspawn/nspawn-expose-ports.h3
-rw-r--r--src/nspawn/nspawn-gperf.gperf43
-rw-r--r--src/nspawn/nspawn-mount.c129
-rw-r--r--src/nspawn/nspawn-mount.h3
-rw-r--r--src/nspawn/nspawn-network.c192
-rw-r--r--src/nspawn/nspawn-network.h6
-rw-r--r--src/nspawn/nspawn-register.c27
-rw-r--r--src/nspawn/nspawn-register.h2
-rw-r--r--src/nspawn/nspawn-settings.c68
-rw-r--r--src/nspawn/nspawn-settings.h8
-rw-r--r--src/nspawn/nspawn-setuid.c17
-rw-r--r--src/nspawn/nspawn.c299
-rw-r--r--src/nss-myhostname/nss-myhostname.c10
-rw-r--r--src/nss-mymachines/nss-mymachines.c53
-rw-r--r--src/nss-resolve/nss-resolve.c31
-rw-r--r--src/path/path.c14
-rw-r--r--src/quotacheck/quotacheck.c10
-rw-r--r--src/random-seed/random-seed.c10
-rw-r--r--src/rc-local-generator/rc-local-generator.c7
-rw-r--r--src/remount-fs/remount-fs.c77
-rw-r--r--src/reply-password/reply-password.c27
-rw-r--r--src/resolve-host/resolve-host.c800
-rw-r--r--src/resolve/RFCs59
-rw-r--r--src/resolve/dns-type.c167
-rw-r--r--src/resolve/dns-type.h31
-rw-r--r--src/resolve/resolved-bus.c1283
-rw-r--r--src/resolve/resolved-bus.h1
-rw-r--r--src/resolve/resolved-conf.c165
-rw-r--r--src/resolve/resolved-conf.h12
-rw-r--r--src/resolve/resolved-def.h18
-rw-r--r--src/resolve/resolved-dns-answer.c695
-rw-r--r--src/resolve/resolved-dns-answer.h97
-rw-r--r--src/resolve/resolved-dns-cache.c658
-rw-r--r--src/resolve/resolved-dns-cache.h18
-rw-r--r--src/resolve/resolved-dns-dnssec.c2131
-rw-r--r--src/resolve/resolved-dns-dnssec.h92
-rw-r--r--src/resolve/resolved-dns-packet.c685
-rw-r--r--src/resolve/resolved-dns-packet.h112
-rw-r--r--src/resolve/resolved-dns-query.c891
-rw-r--r--src/resolve/resolved-dns-query.h69
-rw-r--r--src/resolve/resolved-dns-question.c298
-rw-r--r--src/resolve/resolved-dns-question.h32
-rw-r--r--src/resolve/resolved-dns-rr.c712
-rw-r--r--src/resolve/resolved-dns-rr.h176
-rw-r--r--src/resolve/resolved-dns-scope.c323
-rw-r--r--src/resolve/resolved-dns-scope.h32
-rw-r--r--src/resolve/resolved-dns-search-domain.c232
-rw-r--r--src/resolve/resolved-dns-search-domain.h75
-rw-r--r--src/resolve/resolved-dns-server.c631
-rw-r--r--src/resolve/resolved-dns-server.h75
-rw-r--r--src/resolve/resolved-dns-stream.c12
-rw-r--r--src/resolve/resolved-dns-transaction.c2598
-rw-r--r--src/resolve/resolved-dns-transaction.h96
-rw-r--r--src/resolve/resolved-dns-trust-anchor.c745
-rw-r--r--src/resolve/resolved-dns-trust-anchor.h45
-rw-r--r--src/resolve/resolved-dns-zone.c302
-rw-r--r--src/resolve/resolved-dns-zone.h8
-rw-r--r--src/resolve/resolved-gperf.gperf8
-rw-r--r--src/resolve/resolved-link-bus.c528
-rw-r--r--src/resolve/resolved-link-bus.h40
-rw-r--r--src/resolve/resolved-link.c422
-rw-r--r--src/resolve/resolved-link.h29
-rw-r--r--src/resolve/resolved-llmnr.c27
-rw-r--r--src/resolve/resolved-manager.c599
-rw-r--r--src/resolve/resolved-manager.h62
-rw-r--r--src/resolve/resolved-mdns.c289
-rw-r--r--src/resolve/resolved-mdns.h32
-rw-r--r--src/resolve/resolved-resolv-conf.c271
-rw-r--r--src/resolve/resolved-resolv-conf.h (renamed from src/libsystemd-network/lldp-util.h)11
-rw-r--r--src/resolve/resolved.c16
-rw-r--r--src/resolve/resolved.conf.in2
-rw-r--r--src/resolve/test-dnssec-complex.c238
-rw-r--r--src/resolve/test-dnssec.c338
-rw-r--r--src/rfkill/rfkill.c440
-rw-r--r--src/run/run.c158
-rw-r--r--src/shared/acl-util.c50
-rw-r--r--src/shared/acl-util.h3
-rw-r--r--src/shared/acpi-fpdt.c14
-rw-r--r--src/shared/apparmor-util.c6
-rw-r--r--src/shared/architecture.c3
-rw-r--r--src/shared/architecture.h17
-rw-r--r--src/shared/ask-password-api.c399
-rw-r--r--src/shared/ask-password-api.h25
-rw-r--r--src/shared/base-filesystem.c22
-rw-r--r--src/shared/boot-timestamps.c4
-rw-r--r--src/shared/bus-util.c378
-rw-r--r--src/shared/bus-util.h38
-rw-r--r--src/shared/cgroup-show.c18
-rw-r--r--src/shared/cgroup-show.h2
-rw-r--r--src/shared/clean-ipc.c52
-rw-r--r--src/shared/condition.c41
-rw-r--r--src/shared/conf-parser.c169
-rw-r--r--src/shared/conf-parser.h14
-rw-r--r--src/shared/dev-setup.c7
-rw-r--r--src/shared/dns-domain.c867
-rw-r--r--src/shared/dns-domain.h53
-rw-r--r--src/shared/dropin.c30
-rw-r--r--src/shared/dropin.h1
-rw-r--r--src/shared/efivars.c24
-rw-r--r--src/shared/efivars.h3
-rw-r--r--src/shared/firewall-util.c11
-rw-r--r--src/shared/firewall-util.h3
-rw-r--r--src/shared/fstab-util.c85
-rw-r--r--src/shared/fstab-util.h8
-rw-r--r--src/shared/generator.c47
-rw-r--r--src/shared/gpt.h7
-rw-r--r--src/shared/import-util.c82
-rw-r--r--src/shared/import-util.h6
-rw-r--r--src/shared/install-printf.c65
-rw-r--r--src/shared/install.c2054
-rw-r--r--src/shared/install.h53
-rw-r--r--src/shared/logs-show.c44
-rw-r--r--src/shared/logs-show.h6
-rw-r--r--src/shared/machine-image.c67
-rw-r--r--src/shared/machine-image.h8
-rw-r--r--src/shared/machine-pool.c82
-rw-r--r--src/shared/machine-pool.h2
-rw-r--r--src/shared/pager.c46
-rw-r--r--src/shared/path-lookup.c49
-rw-r--r--src/shared/path-lookup.h1
-rw-r--r--src/shared/ptyfwd.c68
-rw-r--r--src/shared/ptyfwd.h14
-rw-r--r--src/shared/resolve-util.c41
-rw-r--r--src/shared/resolve-util.h62
-rw-r--r--src/shared/seccomp-util.c5
-rw-r--r--src/shared/seccomp-util.h1
-rw-r--r--src/shared/sleep-config.c19
-rw-r--r--src/shared/spawn-ask-password-agent.c12
-rw-r--r--src/shared/spawn-polkit-agent.c18
-rw-r--r--src/shared/specifier.c11
-rw-r--r--src/shared/switch-root.c24
-rw-r--r--src/shared/switch-root.h1
-rw-r--r--src/shared/sysctl-util.c13
-rw-r--r--src/shared/uid-range.c7
-rw-r--r--src/shared/utmp-wtmp.c18
-rw-r--r--src/shared/utmp-wtmp.h4
-rw-r--r--src/shared/watchdog.c7
-rw-r--r--src/shared/watchdog.h3
-rw-r--r--src/sleep/sleep.c18
-rw-r--r--src/socket-proxy/socket-proxyd.c17
-rw-r--r--src/sysctl/sysctl.c33
-rw-r--r--src/system-update-generator/system-update-generator.c2
-rw-r--r--src/systemctl/systemctl.c2016
-rw-r--r--src/systemd/_sd-common.h15
-rw-r--r--src/systemd/sd-bus.h13
-rw-r--r--src/systemd/sd-daemon.h4
-rw-r--r--src/systemd/sd-device.h5
-rw-r--r--src/systemd/sd-dhcp-client.h57
-rw-r--r--src/systemd/sd-dhcp-lease.h20
-rw-r--r--src/systemd/sd-dhcp-server.h12
-rw-r--r--src/systemd/sd-dhcp6-client.h54
-rw-r--r--src/systemd/sd-dhcp6-lease.h9
-rw-r--r--src/systemd/sd-event.h17
-rw-r--r--src/systemd/sd-hwdb.h6
-rw-r--r--src/systemd/sd-icmp6-nd.h79
-rw-r--r--src/systemd/sd-ipv4acd.h15
-rw-r--r--src/systemd/sd-ipv4ll.h14
-rw-r--r--src/systemd/sd-journal.h5
-rw-r--r--src/systemd/sd-lldp.h48
-rw-r--r--src/systemd/sd-login.h4
-rw-r--r--src/systemd/sd-messages.h4
-rw-r--r--src/systemd/sd-ndisc.h86
-rw-r--r--src/systemd/sd-netlink.h13
-rw-r--r--src/systemd/sd-network.h25
-rw-r--r--src/systemd/sd-path.h6
-rw-r--r--src/systemd/sd-pppoe.h53
-rw-r--r--src/systemd/sd-resolve.h17
-rw-r--r--src/sysusers/sysusers.c91
-rw-r--r--src/sysv-generator/sysv-generator.c522
-rw-r--r--src/test/test-acl-util.c87
-rw-r--r--src/test/test-af-list.c7
-rw-r--r--src/test/test-architecture.c4
-rw-r--r--src/test/test-arphrd-list.c5
-rw-r--r--src/test/test-async.c3
-rw-r--r--src/test/test-boot-timestamps.c6
-rw-r--r--src/test/test-btrfs.c75
-rw-r--r--src/test/test-calendarspec.c60
-rw-r--r--src/test/test-cap-list.c11
-rw-r--r--src/test/test-capability.c82
-rw-r--r--src/test/test-cgroup-mask.c15
-rw-r--r--src/test/test-cgroup-util.c11
-rw-r--r--src/test/test-cgroup.c3
-rw-r--r--src/test/test-condition.c20
-rw-r--r--src/test/test-conf-files.c11
-rw-r--r--src/test/test-conf-parser.c5
-rw-r--r--src/test/test-copy.c10
-rw-r--r--src/test/test-daemon.c17
-rw-r--r--src/test/test-date.c56
-rw-r--r--src/test/test-device-nodes.c2
-rw-r--r--src/test/test-dns-domain.c397
-rw-r--r--src/test/test-ellipsize.c6
-rw-r--r--src/test/test-engine.c31
-rw-r--r--src/test/test-env-replace.c11
-rw-r--r--src/test/test-execute.c159
-rw-r--r--src/test/test-extract-word.c558
-rw-r--r--src/test/test-fdset.c4
-rw-r--r--src/test/test-fileio.c45
-rw-r--r--src/test/test-firewall-util.c2
-rw-r--r--src/test/test-fstab-util.c4
-rw-r--r--src/test/test-hashmap-plain.c10
-rw-r--r--src/test/test-hashmap.c2
-rw-r--r--src/test/test-helper.h12
-rw-r--r--src/test/test-hostname-util.c4
-rw-r--r--src/test/test-id128.c8
-rw-r--r--src/test/test-install-root.c665
-rw-r--r--src/test/test-install.c73
-rw-r--r--src/test/test-ipcrm.c3
-rw-r--r--src/test/test-job-type.c2
-rw-r--r--src/test/test-json.c4
-rw-r--r--src/test/test-libudev.c9
-rw-r--r--src/test/test-locale-util.c2
-rw-r--r--src/test/test-log.c2
-rw-r--r--src/test/test-loopback.c4
-rw-r--r--src/test/test-namespace.c5
-rw-r--r--src/test/test-netlink-manual.c9
-rw-r--r--src/test/test-ns.c2
-rw-r--r--src/test/test-parse-util.c495
-rw-r--r--src/test/test-path-lookup.c26
-rw-r--r--src/test/test-path-util.c52
-rw-r--r--src/test/test-path.c21
-rw-r--r--src/test/test-prioq.c12
-rw-r--r--src/test/test-process-util.c12
-rw-r--r--src/test/test-ratelimit.c2
-rw-r--r--src/test/test-rbtree.c362
-rw-r--r--src/test/test-replace-var.c3
-rw-r--r--src/test/test-rlimit-util.c69
-rw-r--r--src/test/test-sched-prio.c7
-rw-r--r--src/test/test-sigbus.c3
-rw-r--r--src/test/test-siphash24.c85
-rw-r--r--src/test/test-sleep.c2
-rw-r--r--src/test/test-socket-util.c44
-rw-r--r--src/test/test-strbuf.c1
-rw-r--r--src/test/test-string-util.c107
-rw-r--r--src/test/test-strip-tab-ansi.c3
-rw-r--r--src/test/test-strv.c90
-rw-r--r--src/test/test-strxcpyx.c3
-rw-r--r--src/test/test-tables.c18
-rw-r--r--src/test/test-terminal-util.c8
-rw-r--r--src/test/test-time.c25
-rw-r--r--src/test/test-tmpfiles.c27
-rw-r--r--src/test/test-udev.c14
-rw-r--r--src/test/test-uid-range.c4
-rw-r--r--src/test/test-unaligned.c76
-rw-r--r--src/test/test-unit-file.c277
-rw-r--r--src/test/test-unit-name.c43
-rw-r--r--src/test/test-user-util.c54
-rw-r--r--src/test/test-utf8.c2
-rw-r--r--src/test/test-util.c933
-rw-r--r--src/test/test-watchdog.c2
-rw-r--r--src/test/test-xml.c4
-rw-r--r--src/timedate/timedatectl.c29
-rw-r--r--src/timedate/timedated.c32
-rw-r--r--src/timesync/timesyncd-conf.c30
-rw-r--r--src/timesync/timesyncd-conf.h1
-rw-r--r--src/timesync/timesyncd-manager.c36
-rw-r--r--src/timesync/timesyncd-manager.h3
-rw-r--r--src/timesync/timesyncd-server.c1
-rw-r--r--src/timesync/timesyncd-server.h2
-rw-r--r--src/timesync/timesyncd.c19
-rw-r--r--src/tmpfiles/tmpfiles.c151
-rw-r--r--src/tty-ask-password-agent/tty-ask-password-agent.c247
-rw-r--r--src/udev/.gitignore1
-rw-r--r--src/udev/ata_id/ata_id.c24
-rw-r--r--src/udev/cdrom_id/cdrom_id.c23
-rw-r--r--src/udev/collect/collect.c9
-rw-r--r--src/udev/mtd_probe/mtd_probe.c8
-rw-r--r--src/udev/mtd_probe/probe_smartmedia.c9
-rw-r--r--src/udev/net/ethtool-util.c8
-rw-r--r--src/udev/net/link-config.c34
-rw-r--r--src/udev/net/link-config.h6
-rw-r--r--src/udev/scsi_id/scsi.h64
-rw-r--r--src/udev/scsi_id/scsi_id.c21
-rw-r--r--src/udev/scsi_id/scsi_id.h2
-rw-r--r--src/udev/scsi_id/scsi_serial.c101
-rw-r--r--src/udev/udev-builtin-blkid.c18
-rw-r--r--src/udev/udev-builtin-btrfs.c6
-rw-r--r--src/udev/udev-builtin-hwdb.c10
-rw-r--r--src/udev/udev-builtin-input_id.c24
-rw-r--r--src/udev/udev-builtin-keyboard.c8
-rw-r--r--src/udev/udev-builtin-kmod.c9
-rw-r--r--src/udev/udev-builtin-net_id.c41
-rw-r--r--src/udev/udev-builtin-net_setup_link.c3
-rw-r--r--src/udev/udev-builtin-path_id.c75
-rw-r--r--src/udev/udev-builtin-uaccess.c10
-rw-r--r--src/udev/udev-builtin-usb_id.c15
-rw-r--r--src/udev/udev-builtin.c5
-rw-r--r--src/udev/udev-ctrl.c12
-rw-r--r--src/udev/udev-event.c40
-rw-r--r--src/udev/udev-node.c38
-rw-r--r--src/udev/udev-rules.c41
-rw-r--r--src/udev/udev-watch.c15
-rw-r--r--src/udev/udev.h13
-rw-r--r--src/udev/udevadm-control.c8
-rw-r--r--src/udev/udevadm-hwdb.c19
-rw-r--r--src/udev/udevadm-info.c16
-rw-r--r--src/udev/udevadm-monitor.c19
-rw-r--r--src/udev/udevadm-settle.c20
-rw-r--r--src/udev/udevadm-test-builtin.c9
-rw-r--r--src/udev/udevadm-test.c15
-rw-r--r--src/udev/udevadm-trigger.c13
-rw-r--r--src/udev/udevadm-util.c3
-rw-r--r--src/udev/udevadm-util.h2
-rw-r--r--src/udev/udevadm.c6
-rw-r--r--src/udev/udevd.c68
-rw-r--r--src/udev/v4l_id/v4l_id.c17
-rw-r--r--src/update-done/update-done.c4
-rw-r--r--src/update-utmp/update-utmp.c36
-rw-r--r--src/user-sessions/user-sessions.c13
-rw-r--r--src/vconsole/vconsole-setup.c32
971 files changed, 56842 insertions, 30211 deletions
diff --git a/src/activate/activate.c b/src/activate/activate.c
index 5318829442..b7e6255f49 100644
--- a/src/activate/activate.c
+++ b/src/activate/activate.c
@@ -19,26 +19,29 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
+#include <getopt.h>
#include <sys/epoll.h>
#include <sys/prctl.h>
#include <sys/socket.h>
#include <sys/wait.h>
-#include <getopt.h>
+#include <unistd.h>
-#include "systemd/sd-daemon.h"
+#include "sd-daemon.h"
-#include "socket-util.h"
-#include "build.h"
+#include "alloc-util.h"
+#include "fd-util.h"
#include "log.h"
-#include "strv.h"
#include "macro.h"
#include "signal-util.h"
+#include "socket-util.h"
+#include "string-util.h"
+#include "strv.h"
static char** arg_listen = NULL;
static bool arg_accept = false;
static char** arg_args = NULL;
static char** arg_setenv = NULL;
+static const char *arg_fdname = NULL;
static int add_epoll(int epoll_fd, int fd) {
struct epoll_event ev = {
@@ -137,8 +140,8 @@ static int launch(char* name, char **argv, char **env, int fds) {
length = strv_length(arg_setenv);
- /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, NULL */
- envp = new0(char *, length + 7);
+ /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, LISTEN_FDNAMES, NULL */
+ envp = new0(char *, length + 8);
if (!envp)
return log_oom();
@@ -146,7 +149,9 @@ static int launch(char* name, char **argv, char **env, int fds) {
if (strchr(*s, '='))
envp[n_env++] = *s;
else {
- _cleanup_free_ char *p = strappend(*s, "=");
+ _cleanup_free_ char *p;
+
+ p = strappend(*s, "=");
if (!p)
return log_oom();
envp[n_env] = strv_find_prefix(env, p);
@@ -165,15 +170,37 @@ static int launch(char* name, char **argv, char **env, int fds) {
(asprintf((char**)(envp + n_env++), "LISTEN_PID=%d", getpid()) < 0))
return log_oom();
+ if (arg_fdname) {
+ char *e;
+
+ e = strappend("LISTEN_FDNAMES=", arg_fdname);
+ if (!e)
+ return log_oom();
+
+ for (i = 1; i < (unsigned) fds; i++) {
+ char *c;
+
+ c = strjoin(e, ":", arg_fdname, NULL);
+ if (!c) {
+ free(e);
+ return log_oom();
+ }
+
+ free(e);
+ e = c;
+ }
+
+ envp[n_env++] = e;
+ }
+
tmp = strv_join(argv, " ");
if (!tmp)
return log_oom();
log_info("Execing %s (%s)", name, tmp);
execvpe(name, argv, envp);
- log_error_errno(errno, "Failed to execp %s (%s): %m", name, tmp);
- return -errno;
+ return log_error_errno(errno, "Failed to execp %s (%s): %m", name, tmp);
}
static int launch1(const char* child, char** argv, char **env, int fd) {
@@ -290,6 +317,7 @@ static void help(void) {
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
+ ARG_FDNAME,
};
static const struct option options[] = {
@@ -298,11 +326,12 @@ static int parse_argv(int argc, char *argv[]) {
{ "listen", required_argument, NULL, 'l' },
{ "accept", no_argument, NULL, 'a' },
{ "setenv", required_argument, NULL, 'E' },
- { "environment", required_argument, NULL, 'E' }, /* alias */
+ { "environment", required_argument, NULL, 'E' }, /* legacy alias */
+ { "fdname", required_argument, NULL, ARG_FDNAME },
{}
};
- int c;
+ int c, r;
assert(argc >= 0);
assert(argv);
@@ -314,29 +343,34 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0 /* done */;
+ return version();
- case 'l': {
- int r = strv_extend(&arg_listen, optarg);
+ case 'l':
+ r = strv_extend(&arg_listen, optarg);
if (r < 0)
- return r;
+ return log_oom();
break;
- }
case 'a':
arg_accept = true;
break;
- case 'E': {
- int r = strv_extend(&arg_setenv, optarg);
+ case 'E':
+ r = strv_extend(&arg_setenv, optarg);
if (r < 0)
- return r;
+ return log_oom();
break;
- }
+
+ case ARG_FDNAME:
+ if (!fdname_is_valid(optarg)) {
+ log_error("File descriptor name %s is not valid, refusing.", optarg);
+ return -EINVAL;
+ }
+
+ arg_fdname = optarg;
+ break;
case '?':
return -EINVAL;
diff --git a/src/analyze/analyze-verify.c b/src/analyze/analyze-verify.c
index f4255f979e..3c9766da04 100644
--- a/src/analyze/analyze-verify.c
+++ b/src/analyze/analyze-verify.c
@@ -21,12 +21,56 @@
#include <stdlib.h>
-#include "manager.h"
+#include "alloc-util.h"
+#include "analyze-verify.h"
+#include "bus-error.h"
#include "bus-util.h"
#include "log.h"
-#include "strv.h"
+#include "manager.h"
#include "pager.h"
-#include "analyze-verify.h"
+#include "path-util.h"
+#include "strv.h"
+#include "unit-name.h"
+
+static int prepare_filename(const char *filename, char **ret) {
+ int r;
+ const char *name;
+ _cleanup_free_ char *abspath = NULL;
+ _cleanup_free_ char *dir = NULL;
+ _cleanup_free_ char *with_instance = NULL;
+ char *c;
+
+ assert(filename);
+ assert(ret);
+
+ r = path_make_absolute_cwd(filename, &abspath);
+ if (r < 0)
+ return r;
+
+ name = basename(abspath);
+ if (!unit_name_is_valid(name, UNIT_NAME_ANY))
+ return -EINVAL;
+
+ if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) {
+ r = unit_name_replace_instance(name, "i", &with_instance);
+ if (r < 0)
+ return r;
+ }
+
+ dir = dirname_malloc(abspath);
+ if (!dir)
+ return -ENOMEM;
+
+ if (with_instance)
+ c = path_join(NULL, dir, with_instance);
+ else
+ c = path_join(NULL, dir, name);
+ if (!c)
+ return -ENOMEM;
+
+ *ret = c;
+ return 0;
+}
static int generate_path(char **var, char **filenames) {
char **filename;
@@ -161,8 +205,7 @@ static int verify_documentation(Unit *u, bool check_man) {
}
static int verify_unit(Unit *u, bool check_man) {
- _cleanup_bus_error_free_ sd_bus_error err = SD_BUS_ERROR_NULL;
- Job *j;
+ _cleanup_(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL;
int r, k;
assert(u);
@@ -171,11 +214,9 @@ static int verify_unit(Unit *u, bool check_man) {
unit_dump(u, stdout, "\t");
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, "Error: %s: %s", err.name, err.message);
+ r = manager_add_job(u->manager, JOB_START, u, JOB_REPLACE, &err, NULL);
if (r < 0)
- log_unit_error_errno(u, r, "Failed to create %s/start: %m", u->id);
+ log_unit_error_errno(u, r, "Failed to create %s/start: %s", u->id, bus_error_message(&err, r));
k = verify_socket(u);
if (k < 0 && r == 0)
@@ -193,7 +234,7 @@ static int verify_unit(Unit *u, 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;
+ _cleanup_(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL;
Manager *m = NULL;
FILE *serial = NULL;
FDSet *fdset = NULL;
@@ -233,18 +274,19 @@ int verify_units(char **filenames, ManagerRunningAs running_as, bool check_man)
log_debug("Loading remaining units from the command line...");
STRV_FOREACH(filename, filenames) {
- char fname[UNIT_NAME_MAX + 2 + 1] = "./";
+ _cleanup_free_ char *prepared = NULL;
log_debug("Handling %s...", *filename);
- /* manager_load_unit does not like pure basenames, so prepend
- * the local directory, but only for valid names. manager_load_unit
- * will print the error for other ones. */
- if (!strchr(*filename, '/') && strlen(*filename) <= UNIT_NAME_MAX) {
- strncat(fname + 2, *filename, UNIT_NAME_MAX);
- k = manager_load_unit(m, NULL, fname, &err, &units[count]);
- } else
- k = manager_load_unit(m, NULL, *filename, &err, &units[count]);
+ k = prepare_filename(*filename, &prepared);
+ if (k < 0) {
+ log_error_errno(k, "Failed to prepare filename %s: %m", *filename);
+ if (r == 0)
+ r = k;
+ continue;
+ }
+
+ k = manager_load_unit(m, NULL, prepared, &err, &units[count]);
if (k < 0) {
log_error_errno(k, "Failed to load %s: %m", *filename);
if (r == 0)
diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c
index 3657ef50f1..c4e22786b1 100644
--- a/src/analyze/analyze.c
+++ b/src/analyze/analyze.c
@@ -20,25 +20,29 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
-#include <stdlib.h>
#include <getopt.h>
#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
#include "sd-bus.h"
-#include "bus-util.h"
+
+#include "alloc-util.h"
+#include "analyze-verify.h"
#include "bus-error.h"
-#include "log.h"
-#include "build.h"
-#include "util.h"
-#include "strxcpyx.h"
-#include "strv.h"
-#include "unit-name.h"
-#include "special.h"
+#include "bus-util.h"
+#include "glob-util.h"
#include "hashmap.h"
+#include "locale-util.h"
+#include "log.h"
#include "pager.h"
-#include "analyze-verify.h"
+#include "parse-util.h"
+#include "special.h"
+#include "strv.h"
+#include "strxcpyx.h"
#include "terminal-util.h"
+#include "unit-name.h"
+#include "util.h"
#define SCALE_X (0.1 / 1000.0) /* pixels per us */
#define SCALE_Y (20.0)
@@ -130,7 +134,7 @@ static void pager_open_if_enabled(void) {
}
static int bus_get_uint64_property(sd_bus *bus, const char *path, const char *interface, const char *property, uint64_t *val) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert(bus);
@@ -157,7 +161,7 @@ static int bus_get_uint64_property(sd_bus *bus, const char *path, const char *in
}
static int bus_get_unit_property_strv(sd_bus *bus, const char *path, const char *property, char ***strv) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert(bus);
@@ -318,6 +322,10 @@ finish:
}
static void free_host_info(struct host_info *hi) {
+
+ if (!hi)
+ return;
+
free(hi->hostname);
free(hi->kernel_name);
free(hi->kernel_release);
@@ -328,9 +336,11 @@ static void free_host_info(struct host_info *hi) {
free(hi);
}
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct host_info*, free_host_info);
+
static int acquire_time_data(sd_bus *bus, struct unit_times **out) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r, c = 0;
struct boot_times *boot_times = NULL;
struct unit_times *unit_times = NULL;
@@ -430,24 +440,25 @@ fail:
}
static int acquire_host_info(sd_bus *bus, struct host_info **hi) {
- int r;
- struct host_info *host;
-
static const struct bus_properties_map hostname_map[] = {
- { "Hostname", "s", NULL, offsetof(struct host_info, hostname) },
- { "KernelName", "s", NULL, offsetof(struct host_info, kernel_name) },
- { "KernelRelease", "s", NULL, offsetof(struct host_info, kernel_release) },
- { "KernelVersion", "s", NULL, offsetof(struct host_info, kernel_version) },
+ { "Hostname", "s", NULL, offsetof(struct host_info, hostname) },
+ { "KernelName", "s", NULL, offsetof(struct host_info, kernel_name) },
+ { "KernelRelease", "s", NULL, offsetof(struct host_info, kernel_release) },
+ { "KernelVersion", "s", NULL, offsetof(struct host_info, kernel_version) },
{ "OperatingSystemPrettyName", "s", NULL, offsetof(struct host_info, os_pretty_name) },
{}
};
static const struct bus_properties_map manager_map[] = {
- { "Virtualization", "s", NULL, offsetof(struct host_info, virtualization) },
- { "Architecture", "s", NULL, offsetof(struct host_info, architecture) },
+ { "Virtualization", "s", NULL, offsetof(struct host_info, virtualization) },
+ { "Architecture", "s", NULL, offsetof(struct host_info, architecture) },
{}
};
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(free_host_infop) struct host_info *host;
+ int r;
+
host = new0(struct host_info, 1);
if (!host)
return log_oom();
@@ -458,7 +469,7 @@ static int acquire_host_info(sd_bus *bus, struct host_info **hi) {
hostname_map,
host);
if (r < 0)
- goto fail;
+ log_debug_errno(r, "Failed to get host information from systemd-hostnamed: %s", bus_error_message(&error, r));
r = bus_map_all_properties(bus,
"org.freedesktop.systemd1",
@@ -466,13 +477,12 @@ static int acquire_host_info(sd_bus *bus, struct host_info **hi) {
manager_map,
host);
if (r < 0)
- goto fail;
+ return log_error_errno(r, "Failed to get host information from systemd: %s", bus_error_message(&error, r));
*hi = host;
+ host = NULL;
+
return 0;
-fail:
- free_host_info(host);
- return r;
}
static int pretty_boot_time(sd_bus *bus, char **_buf) {
@@ -535,9 +545,9 @@ static void svg_graph_box(double height, double begin, double end) {
}
static int analyze_plot(sd_bus *bus) {
+ _cleanup_(free_host_infop) struct host_info *host = NULL;
struct unit_times *times;
struct boot_times *boot;
- struct host_info *host = NULL;
int n, m = 1, y=0;
double width;
_cleanup_free_ char *pretty_times = NULL;
@@ -557,7 +567,7 @@ static int analyze_plot(sd_bus *bus) {
n = acquire_time_data(bus, &times);
if (n <= 0)
- goto out;
+ return n;
qsort(times, n, sizeof(struct unit_times), compare_unit_start);
@@ -653,12 +663,12 @@ static int analyze_plot(sd_bus *bus) {
svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times);
svg("<text x=\"20\" y=\"30\">%s %s (%s %s %s) %s %s</text>",
isempty(host->os_pretty_name) ? "Linux" : host->os_pretty_name,
- isempty(host->hostname) ? "" : host->hostname,
- isempty(host->kernel_name) ? "" : host->kernel_name,
- isempty(host->kernel_release) ? "" : host->kernel_release,
- isempty(host->kernel_version) ? "" : host->kernel_version,
- isempty(host->architecture) ? "" : host->architecture,
- isempty(host->virtualization) ? "" : host->virtualization);
+ strempty(host->hostname),
+ strempty(host->kernel_name),
+ strempty(host->kernel_release),
+ strempty(host->kernel_version),
+ strempty(host->architecture),
+ strempty(host->virtualization));
svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
svg_graph_box(m, -(double) boot->firmware_time, boot->finish_time);
@@ -742,8 +752,6 @@ static int analyze_plot(sd_bus *bus) {
free_unit_times(times, (unsigned) n);
n = 0;
-out:
- free_host_info(host);
return n;
}
@@ -891,8 +899,8 @@ static int list_dependencies(sd_bus *bus, const char *name) {
int r;
const char *id;
_cleanup_free_ char *path = NULL;
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
struct boot_times *boot;
assert(bus);
@@ -1058,20 +1066,17 @@ static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[], char *fro
assert(bus);
assert(u);
- if (arg_dot == DEP_ORDER ||arg_dot == DEP_ALL) {
+ if (IN_SET(arg_dot, DEP_ORDER, DEP_ALL)) {
r = graph_one_property(bus, u, "After", "green", patterns, from_patterns, to_patterns);
if (r < 0)
return r;
}
- if (arg_dot == DEP_REQUIRE ||arg_dot == DEP_ALL) {
+ if (IN_SET(arg_dot, DEP_REQUIRE, DEP_ALL)) {
r = graph_one_property(bus, u, "Requires", "black", patterns, from_patterns, to_patterns);
if (r < 0)
return r;
- r = graph_one_property(bus, u, "RequiresOverridable", "black", patterns, from_patterns, to_patterns);
- if (r < 0)
- return r;
- r = graph_one_property(bus, u, "RequisiteOverridable", "darkblue", patterns, from_patterns, to_patterns);
+ r = graph_one_property(bus, u, "Requisite", "darkblue", patterns, from_patterns, to_patterns);
if (r < 0)
return r;
r = graph_one_property(bus, u, "Wants", "grey66", patterns, from_patterns, to_patterns);
@@ -1080,9 +1085,6 @@ static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[], char *fro
r = graph_one_property(bus, u, "Conflicts", "red", patterns, from_patterns, to_patterns);
if (r < 0)
return r;
- r = graph_one_property(bus, u, "ConflictedBy", "red", patterns, from_patterns, to_patterns);
- if (r < 0)
- return r;
}
return 0;
@@ -1094,7 +1096,7 @@ static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) {
int r;
STRV_FOREACH(pattern, patterns) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_free_ char *unit = NULL, *unit_id = NULL;
if (strv_extend(&expanded_patterns, *pattern) < 0)
@@ -1131,8 +1133,8 @@ static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) {
}
static int dot(sd_bus *bus, char* patterns[]) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_strv_free_ char **expanded_patterns = NULL;
_cleanup_strv_free_ char **expanded_from_patterns = NULL;
_cleanup_strv_free_ char **expanded_to_patterns = NULL;
@@ -1196,8 +1198,8 @@ static int dot(sd_bus *bus, char* patterns[]) {
}
static int dump(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_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
const char *text = NULL;
int r;
@@ -1217,10 +1219,8 @@ static int dump(sd_bus *bus, char **args) {
&error,
&reply,
"");
- if (r < 0) {
- log_error("Failed issue method call: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed issue method call: %s", bus_error_message(&error, r));
r = sd_bus_message_read(reply, "s", &text);
if (r < 0)
@@ -1231,7 +1231,7 @@ static int dump(sd_bus *bus, char **args) {
}
static int set_log_level(sd_bus *bus, char **args) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert(bus);
@@ -1251,11 +1251,36 @@ static int set_log_level(sd_bus *bus, char **args) {
&error,
"s",
args[0]);
- if (r < 0) {
- log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
- return -EIO;
+ if (r < 0)
+ return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
+
+ return 0;
+}
+
+static int set_log_target(sd_bus *bus, char **args) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ int r;
+
+ assert(bus);
+ assert(args);
+
+ if (strv_length(args) != 1) {
+ log_error("This command expects one argument only.");
+ return -E2BIG;
}
+ r = sd_bus_set_property(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "LogTarget",
+ &error,
+ "s",
+ args[0]);
+ if (r < 0)
+ return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
+
return 0;
}
@@ -1285,7 +1310,8 @@ static void help(void) {
" critical-chain Print a tree of the time critical chain of units\n"
" plot Output SVG graphic showing service initialization\n"
" dot Output dependency graph in dot(1) format\n"
- " set-log-level LEVEL Set logging threshold for systemd\n"
+ " set-log-level LEVEL Set logging threshold for manager\n"
+ " set-log-target TARGET Set logging target for manager\n"
" dump Output state serialization of service manager\n"
" verify FILE... Check unit files for correctness\n"
, program_invocation_short_name);
@@ -1339,9 +1365,7 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case ARG_USER:
arg_user = true;
@@ -1432,9 +1456,9 @@ int main(int argc, char *argv[]) {
arg_user ? MANAGER_USER : MANAGER_SYSTEM,
arg_man);
else {
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- r = bus_open_transport_systemd(arg_transport, arg_host, arg_user, &bus);
+ r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus);
if (r < 0) {
log_error_errno(r, "Failed to create bus connection: %m");
goto finish;
@@ -1454,6 +1478,8 @@ int main(int argc, char *argv[]) {
r = dump(bus, argv+optind+1);
else if (streq(argv[optind], "set-log-level"))
r = set_log_level(bus, argv+optind+1);
+ else if (streq(argv[optind], "set-log-target"))
+ r = set_log_target(bus, argv+optind+1);
else
log_error("Unknown operation '%s'.", argv[optind]);
}
diff --git a/src/ask-password/ask-password.c b/src/ask-password/ask-password.c
index abfd545c79..a544866000 100644
--- a/src/ask-password/ask-password.c
+++ b/src/ask-password/ask-password.c
@@ -20,36 +20,36 @@
***/
#include <errno.h>
-#include <unistd.h>
#include <getopt.h>
#include <stddef.h>
+#include <unistd.h>
+#include "ask-password-api.h"
+#include "def.h"
#include "log.h"
#include "macro.h"
#include "strv.h"
-#include "ask-password-api.h"
-#include "def.h"
static const char *arg_icon = NULL;
static const char *arg_id = NULL;
-static const char *arg_message = NULL;
-static bool arg_echo = false;
-static bool arg_use_tty = true;
+static const char *arg_keyname = NULL;
+static char *arg_message = NULL;
static usec_t arg_timeout = DEFAULT_TIMEOUT_USEC;
-static bool arg_accept_cached = false;
static bool arg_multiple = false;
+static AskPasswordFlags arg_flags = ASK_PASSWORD_PUSH_CACHE;
static void help(void) {
printf("%s [OPTIONS...] MESSAGE\n\n"
"Query the user for a system passphrase, via the TTY or an UI agent.\n\n"
- " -h --help Show this help\n"
- " --icon=NAME Icon name\n"
- " --timeout=SEC Timeout in sec\n"
- " --echo Do not mask input (useful for usernames)\n"
- " --no-tty Ask question via agent even on TTY\n"
- " --accept-cached Accept cached passwords\n"
- " --multiple List multiple passwords if available\n"
- " --id=ID Query identifier (e.g. cryptsetup:/dev/sda5)\n"
+ " -h --help Show this help\n"
+ " --icon=NAME Icon name\n"
+ " --id=ID Query identifier (e.g. \"cryptsetup:/dev/sda5\")\n"
+ " --keyname=NAME Kernel key name for caching passwords (e.g. \"cryptsetup\")\n"
+ " --timeout=SEC Timeout in seconds\n"
+ " --echo Do not mask input (useful for usernames)\n"
+ " --no-tty Ask question via agent even on TTY\n"
+ " --accept-cached Accept cached passwords\n"
+ " --multiple List multiple passwords if available\n"
, program_invocation_short_name);
}
@@ -62,7 +62,8 @@ static int parse_argv(int argc, char *argv[]) {
ARG_NO_TTY,
ARG_ACCEPT_CACHED,
ARG_MULTIPLE,
- ARG_ID
+ ARG_ID,
+ ARG_KEYNAME,
};
static const struct option options[] = {
@@ -74,6 +75,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "accept-cached", no_argument, NULL, ARG_ACCEPT_CACHED },
{ "multiple", no_argument, NULL, ARG_MULTIPLE },
{ "id", required_argument, NULL, ARG_ID },
+ { "keyname", required_argument, NULL, ARG_KEYNAME },
{}
};
@@ -102,15 +104,15 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_ECHO:
- arg_echo = true;
+ arg_flags |= ASK_PASSWORD_ECHO;
break;
case ARG_NO_TTY:
- arg_use_tty = false;
+ arg_flags |= ASK_PASSWORD_NO_TTY;
break;
case ARG_ACCEPT_CACHED:
- arg_accept_cached = true;
+ arg_flags |= ASK_PASSWORD_ACCEPT_CACHED;
break;
case ARG_MULTIPLE:
@@ -121,6 +123,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_id = optarg;
break;
+ case ARG_KEYNAME:
+ arg_keyname = optarg;
+ break;
+
case '?':
return -EINVAL;
@@ -128,18 +134,20 @@ static int parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option");
}
- if (optind != argc - 1) {
- log_error("%s: required argument missing.", program_invocation_short_name);
- return -EINVAL;
+ if (argc > optind) {
+ arg_message = strv_join(argv + optind, " ");
+ if (!arg_message)
+ return log_oom();
}
- arg_message = argv[optind];
return 1;
}
int main(int argc, char *argv[]) {
- int r;
+ _cleanup_strv_free_erase_ char **l = NULL;
usec_t timeout;
+ char **p;
+ int r;
log_parse_environment();
log_open();
@@ -153,36 +161,21 @@ int main(int argc, char *argv[]) {
else
timeout = 0;
- if (arg_use_tty && isatty(STDIN_FILENO)) {
- char *password = NULL;
-
- r = ask_password_tty(arg_message, timeout, arg_echo, NULL,
- &password);
- if (r >= 0) {
- puts(password);
- free(password);
- }
-
- } else {
- char **l;
-
- r = ask_password_agent(arg_message, arg_icon, arg_id, timeout,
- arg_echo, arg_accept_cached, &l);
- if (r >= 0) {
- char **p;
-
- STRV_FOREACH(p, l) {
- puts(*p);
+ r = ask_password_auto(arg_message, arg_icon, arg_id, arg_keyname, timeout, arg_flags, &l);
+ if (r < 0) {
+ log_error_errno(r, "Failed to query password: %m");
+ goto finish;
+ }
- if (!arg_multiple)
- break;
- }
+ STRV_FOREACH(p, l) {
+ puts(*p);
- strv_free(l);
- }
+ if (!arg_multiple)
+ break;
}
finish:
+ free(arg_message);
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/backlight/backlight.c b/src/backlight/backlight.c
index c8961de946..a59459bc26 100644
--- a/src/backlight/backlight.c
+++ b/src/backlight/backlight.c
@@ -19,12 +19,18 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
-#include "mkdir.h"
-#include "fileio.h"
#include "libudev.h"
-#include "udev-util.h"
+
+#include "alloc-util.h"
#include "def.h"
+#include "escape.h"
+#include "fileio.h"
+#include "mkdir.h"
+#include "parse-util.h"
+#include "proc-cmdline.h"
+#include "string-util.h"
+#include "udev-util.h"
+#include "util.h"
static struct udev_device *find_pci_or_platform_parent(struct udev_device *device) {
struct udev_device *parent;
@@ -317,7 +323,7 @@ int main(int argc, char *argv[]) {
errno = 0;
device = udev_device_new_from_subsystem_sysname(udev, ss, sysname);
if (!device) {
- if (errno != 0)
+ if (errno > 0)
log_error_errno(errno, "Failed to get backlight or LED device '%s:%s': %m", ss, sysname);
else
log_oom();
@@ -375,7 +381,7 @@ int main(int argc, char *argv[]) {
_cleanup_free_ char *value = NULL;
const char *clamp;
- if (!shall_restore_state())
+ if (shall_restore_state() == 0)
return EXIT_SUCCESS;
if (!validate_device(udev, device))
diff --git a/src/basic/af-list.c b/src/basic/af-list.c
index f396115a34..606bb49a59 100644
--- a/src/basic/af-list.c
+++ b/src/basic/af-list.c
@@ -19,16 +19,16 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/socket.h>
#include <string.h>
+#include <sys/socket.h>
-#include "util.h"
#include "af-list.h"
+#include "macro.h"
static const struct af_name* lookup_af(register const char *str, register unsigned int len);
-#include "af-to-name.h"
#include "af-from-name.h"
+#include "af-to-name.h"
const char *af_to_name(int id) {
diff --git a/src/basic/alloc-util.c b/src/basic/alloc-util.c
new file mode 100644
index 0000000000..0c6a15c958
--- /dev/null
+++ b/src/basic/alloc-util.c
@@ -0,0 +1,85 @@
+/*-*- 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 <stdint.h>
+#include <string.h>
+
+#include "alloc-util.h"
+#include "macro.h"
+#include "util.h"
+
+void* memdup(const void *p, size_t l) {
+ void *r;
+
+ assert(p);
+
+ r = malloc(l);
+ if (!r)
+ return NULL;
+
+ memcpy(r, p, l);
+ return r;
+}
+
+void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size) {
+ size_t a, newalloc;
+ void *q;
+
+ assert(p);
+ assert(allocated);
+
+ if (*allocated >= need)
+ return *p;
+
+ newalloc = MAX(need * 2, 64u / size);
+ a = newalloc * size;
+
+ /* check for overflows */
+ if (a < size * need)
+ return NULL;
+
+ q = realloc(*p, a);
+ if (!q)
+ return NULL;
+
+ *p = q;
+ *allocated = newalloc;
+ return q;
+}
+
+void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size) {
+ size_t prev;
+ uint8_t *q;
+
+ assert(p);
+ assert(allocated);
+
+ prev = *allocated;
+
+ q = greedy_realloc(p, allocated, need, size);
+ if (!q)
+ return NULL;
+
+ if (*allocated > prev)
+ memzero(q + prev * size, (*allocated - prev) * size);
+
+ return q;
+}
diff --git a/src/basic/alloc-util.h b/src/basic/alloc-util.h
new file mode 100644
index 0000000000..f5097ea117
--- /dev/null
+++ b/src/basic/alloc-util.h
@@ -0,0 +1,109 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <alloca.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "macro.h"
+
+#define new(t, n) ((t*) malloc_multiply(sizeof(t), (n)))
+
+#define new0(t, n) ((t*) calloc((n), sizeof(t)))
+
+#define newa(t, n) ((t*) alloca(sizeof(t)*(n)))
+
+#define newa0(t, n) ((t*) alloca0(sizeof(t)*(n)))
+
+#define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n)))
+
+#define malloc0(n) (calloc(1, (n)))
+
+static inline void *mfree(void *memory) {
+ free(memory);
+ return NULL;
+}
+
+void* memdup(const void *p, size_t l) _alloc_(2);
+
+static inline void freep(void *p) {
+ free(*(void**) p);
+}
+
+#define _cleanup_free_ _cleanup_(freep)
+
+_malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t a, size_t b) {
+ if (_unlikely_(b != 0 && a > ((size_t) -1) / b))
+ return NULL;
+
+ return malloc(a * b);
+}
+
+_alloc_(2, 3) static inline void *realloc_multiply(void *p, size_t a, size_t b) {
+ if (_unlikely_(b != 0 && a > ((size_t) -1) / b))
+ return NULL;
+
+ return realloc(p, a * b);
+}
+
+_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t a, size_t b) {
+ if (_unlikely_(b != 0 && a > ((size_t) -1) / b))
+ return NULL;
+
+ return memdup(p, a * b);
+}
+
+void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size);
+void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size);
+
+#define GREEDY_REALLOC(array, allocated, need) \
+ greedy_realloc((void**) &(array), &(allocated), (need), sizeof((array)[0]))
+
+#define GREEDY_REALLOC0(array, allocated, need) \
+ greedy_realloc0((void**) &(array), &(allocated), (need), sizeof((array)[0]))
+
+#define alloca0(n) \
+ ({ \
+ char *_new_; \
+ size_t _len_ = n; \
+ _new_ = alloca(_len_); \
+ (void *) memset(_new_, 0, _len_); \
+ })
+
+/* It's not clear what alignment glibc/gcc alloca() guarantee, hence provide a guaranteed safe version */
+#define alloca_align(size, align) \
+ ({ \
+ void *_ptr_; \
+ size_t _mask_ = (align) - 1; \
+ _ptr_ = alloca((size) + _mask_); \
+ (void*)(((uintptr_t)_ptr_ + _mask_) & ~_mask_); \
+ })
+
+#define alloca0_align(size, align) \
+ ({ \
+ void *_new_; \
+ size_t _size_ = (size); \
+ _new_ = alloca_align(_size_, (align)); \
+ (void*)memset(_new_, 0, _size_); \
+ })
diff --git a/src/basic/arphrd-list.c b/src/basic/arphrd-list.c
index 284043cd90..c1fdbbd905 100644
--- a/src/basic/arphrd-list.c
+++ b/src/basic/arphrd-list.c
@@ -22,13 +22,13 @@
#include <net/if_arp.h>
#include <string.h>
-#include "util.h"
#include "arphrd-list.h"
+#include "macro.h"
static const struct arphrd_name* lookup_arphrd(register const char *str, register unsigned int len);
-#include "arphrd-to-name.h"
#include "arphrd-from-name.h"
+#include "arphrd-to-name.h"
const char *arphrd_to_name(int id) {
diff --git a/src/basic/async.c b/src/basic/async.c
index 7725e6d7d3..42c66a762e 100644
--- a/src/basic/async.c
+++ b/src/basic/async.c
@@ -19,11 +19,15 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
#include <pthread.h>
+#include <stddef.h>
#include <unistd.h>
#include "async.h"
+#include "fd-util.h"
#include "log.h"
+#include "macro.h"
#include "util.h"
int asynchronous_job(void* (*func)(void *p), void *arg) {
@@ -67,7 +71,7 @@ int asynchronous_sync(void) {
}
static void *close_thread(void *p) {
- assert_se(close_nointr(PTR_TO_INT(p)) != -EBADF);
+ assert_se(close_nointr(PTR_TO_FD(p)) != -EBADF);
return NULL;
}
@@ -83,7 +87,7 @@ int asynchronous_close(int fd) {
if (fd >= 0) {
PROTECT_ERRNO;
- r = asynchronous_job(close_thread, INT_TO_PTR(fd));
+ r = asynchronous_job(close_thread, FD_TO_PTR(fd));
if (r < 0)
assert_se(close_nointr(fd) != -EBADF);
}
diff --git a/src/basic/audit.c b/src/basic/audit-util.c
index 1f593aa813..e52ee24c05 100644
--- a/src/basic/audit.c
+++ b/src/basic/audit-util.c
@@ -20,13 +20,18 @@
***/
#include <errno.h>
+#include <linux/netlink.h>
#include <stdio.h>
+#include <sys/socket.h>
+#include "alloc-util.h"
+#include "audit-util.h"
+#include "fd-util.h"
+#include "fileio.h"
#include "macro.h"
-#include "audit.h"
-#include "util.h"
+#include "parse-util.h"
#include "process-util.h"
-#include "fileio.h"
+#include "user-util.h"
int audit_session_from_pid(pid_t pid, uint32_t *id) {
_cleanup_free_ char *s = NULL;
diff --git a/src/basic/audit.h b/src/basic/audit-util.h
index 6de331c73e..026d3cd9b1 100644
--- a/src/basic/audit.h
+++ b/src/basic/audit-util.h
@@ -21,8 +21,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdint.h>
#include <stdbool.h>
+#include <stdint.h>
#include <sys/types.h>
#define AUDIT_SESSION_INVALID ((uint32_t) -1)
diff --git a/src/basic/barrier.c b/src/basic/barrier.c
index 436ba95989..26ae123341 100644
--- a/src/basic/barrier.c
+++ b/src/basic/barrier.c
@@ -30,8 +30,8 @@
#include <unistd.h>
#include "barrier.h"
+#include "fd-util.h"
#include "macro.h"
-#include "util.h"
/**
* Barriers
@@ -197,6 +197,7 @@ static bool barrier_write(Barrier *b, uint64_t buf) {
if (barrier_i_aborted(b))
return false;
+ assert(b->me >= 0);
do {
len = write(b->me, &buf, sizeof(buf));
} while (len < 0 && IN_SET(errno, EAGAIN, EINTR));
diff --git a/src/basic/barrier.h b/src/basic/barrier.h
index b8954694d3..722effe834 100644
--- a/src/basic/barrier.h
+++ b/src/basic/barrier.h
@@ -21,6 +21,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <stdbool.h>
+#include <stdint.h>
#include <sys/types.h>
#include "macro.h"
diff --git a/src/basic/bitmap.c b/src/basic/bitmap.c
index 2eabf3e1c1..50078822a7 100644
--- a/src/basic/bitmap.c
+++ b/src/basic/bitmap.c
@@ -19,9 +19,16 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
+#include <errno.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include "alloc-util.h"
#include "bitmap.h"
+#include "hashmap.h"
+#include "macro.h"
struct Bitmap {
uint64_t *bitmaps;
@@ -133,7 +140,8 @@ bool bitmap_isset(Bitmap *b, unsigned n) {
bool bitmap_isclear(Bitmap *b) {
unsigned i;
- assert(b);
+ if (!b)
+ return true;
for (i = 0; i < b->n_bitmaps; i++)
if (b->bitmaps[i] != 0)
@@ -143,7 +151,9 @@ bool bitmap_isclear(Bitmap *b) {
}
void bitmap_clear(Bitmap *b) {
- assert(b);
+
+ if (!b)
+ return;
b->bitmaps = mfree(b->bitmaps);
b->n_bitmaps = 0;
@@ -190,7 +200,10 @@ bool bitmap_equal(Bitmap *a, Bitmap *b) {
Bitmap *c;
unsigned i;
- if (!a ^ !b)
+ if (a == b)
+ return true;
+
+ if (!a != !b)
return false;
if (!a)
diff --git a/src/basic/bitmap.h b/src/basic/bitmap.h
index 2874bc99f7..d2726630f1 100644
--- a/src/basic/bitmap.h
+++ b/src/basic/bitmap.h
@@ -21,8 +21,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "macro.h"
+#include <stdbool.h>
+
#include "hashmap.h"
+#include "macro.h"
typedef struct Bitmap Bitmap;
diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c
index 074deeccda..d07d1df5a8 100644
--- a/src/basic/btrfs-util.c
+++ b/src/basic/btrfs-util.c
@@ -19,24 +19,40 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <linux/loop.h>
+#include <stddef.h>
+#include <stdio.h>
#include <stdlib.h>
-#include <sys/vfs.h>
+#include <string.h>
+#include <sys/ioctl.h>
#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <sys/sysmacros.h>
+#include <unistd.h>
#ifdef HAVE_LINUX_BTRFS_H
#include <linux/btrfs.h>
#endif
+#include "alloc-util.h"
+#include "btrfs-ctree.h"
+#include "btrfs-util.h"
+#include "copy.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "macro.h"
#include "missing.h"
-#include "util.h"
#include "path-util.h"
-#include "macro.h"
-#include "copy.h"
#include "selinux-util.h"
#include "smack-util.h"
-#include "fileio.h"
-#include "btrfs-ctree.h"
-#include "btrfs-util.h"
+#include "sparse-endian.h"
+#include "stat-util.h"
+#include "string-util.h"
+#include "time-util.h"
+#include "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
@@ -59,13 +75,13 @@ static int validate_subvolume_name(const char *name) {
static int open_parent(const char *path, int flags) {
_cleanup_free_ char *parent = NULL;
- int r, fd;
+ int fd;
assert(path);
- r = path_get_parent(path, &parent);
- if (r < 0)
- return r;
+ parent = dirname_malloc(path);
+ if (!parent)
+ return -ENOMEM;
fd = open(parent, flags);
if (fd < 0)
@@ -102,7 +118,7 @@ int btrfs_is_filesystem(int fd) {
return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
}
-int btrfs_is_subvol(int fd) {
+int btrfs_is_subvol_fd(int fd) {
struct stat st;
assert(fd >= 0);
@@ -118,6 +134,18 @@ int btrfs_is_subvol(int fd) {
return btrfs_is_filesystem(fd);
}
+int btrfs_is_subvol(const char *path) {
+ _cleanup_close_ int fd = -1;
+
+ assert(path);
+
+ fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ if (fd < 0)
+ return -errno;
+
+ return btrfs_is_subvol_fd(fd);
+}
+
int btrfs_subvol_make(const char *path) {
struct btrfs_ioctl_vol_args args = {};
_cleanup_close_ int fd = -1;
@@ -436,7 +464,7 @@ static int btrfs_ioctl_search_args_compare(const struct btrfs_ioctl_search_args
#define BTRFS_IOCTL_SEARCH_HEADER_BODY(sh) \
((void*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header)))
-int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *ret) {
+int btrfs_subvol_get_info_fd(int fd, uint64_t subvol_id, BtrfsSubvolInfo *ret) {
struct btrfs_ioctl_search_args args = {
/* Tree of tree roots */
.key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
@@ -453,16 +481,23 @@ int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *ret) {
.key.max_transid = (uint64_t) -1,
};
- uint64_t subvol_id;
bool found = false;
int r;
assert(fd >= 0);
assert(ret);
- r = btrfs_subvol_get_id_fd(fd, &subvol_id);
- if (r < 0)
- return r;
+ if (subvol_id == 0) {
+ r = btrfs_subvol_get_id_fd(fd, &subvol_id);
+ if (r < 0)
+ return r;
+ } else {
+ r = btrfs_is_filesystem(fd);
+ if (r < 0)
+ return r;
+ if (!r)
+ return -ENOTTY;
+ }
args.key.min_objectid = args.key.max_objectid = subvol_id;
@@ -521,7 +556,7 @@ finish:
return 0;
}
-int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *ret) {
+int btrfs_qgroup_get_quota_fd(int fd, uint64_t qgroupid, BtrfsQuotaInfo *ret) {
struct btrfs_ioctl_search_args args = {
/* Tree of quota items */
@@ -540,26 +575,37 @@ int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *ret) {
.key.max_transid = (uint64_t) -1,
};
- uint64_t subvol_id;
bool found_info = false, found_limit = false;
int r;
assert(fd >= 0);
assert(ret);
- r = btrfs_subvol_get_id_fd(fd, &subvol_id);
- if (r < 0)
- return r;
+ if (qgroupid == 0) {
+ r = btrfs_subvol_get_id_fd(fd, &qgroupid);
+ if (r < 0)
+ return r;
+ } else {
+ r = btrfs_is_filesystem(fd);
+ if (r < 0)
+ return r;
+ if (!r)
+ return -ENOTTY;
+ }
- args.key.min_offset = args.key.max_offset = subvol_id;
+ args.key.min_offset = args.key.max_offset = qgroupid;
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)
+ if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) {
+ if (errno == ENOENT) /* quota tree is missing: quota disabled */
+ break;
+
return -errno;
+ }
if (args.key.nr_items <= 0)
break;
@@ -571,7 +617,7 @@ int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *ret) {
if (sh->objectid != 0)
continue;
- if (sh->offset != subvol_id)
+ if (sh->offset != qgroupid)
continue;
if (sh->type == BTRFS_QGROUP_INFO_KEY) {
@@ -585,12 +631,14 @@ 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->referenced_max = le64toh(qli->max_rfer);
- ret->exclusive_max = le64toh(qli->max_excl);
-
- if (ret->referenced_max == 0)
+ if (le64toh(qli->flags) & BTRFS_QGROUP_LIMIT_MAX_RFER)
+ ret->referenced_max = le64toh(qli->max_rfer);
+ else
ret->referenced_max = (uint64_t) -1;
- if (ret->exclusive_max == 0)
+
+ if (le64toh(qli->flags) & BTRFS_QGROUP_LIMIT_MAX_EXCL)
+ ret->exclusive_max = le64toh(qli->max_excl);
+ else
ret->exclusive_max = (uint64_t) -1;
found_limit = true;
@@ -622,6 +670,109 @@ finish:
return 0;
}
+int btrfs_qgroup_get_quota(const char *path, uint64_t qgroupid, BtrfsQuotaInfo *ret) {
+ _cleanup_close_ int fd = -1;
+
+ fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
+ if (fd < 0)
+ return -errno;
+
+ return btrfs_qgroup_get_quota_fd(fd, qgroupid, ret);
+}
+
+int btrfs_subvol_find_subtree_qgroup(int fd, uint64_t subvol_id, uint64_t *ret) {
+ uint64_t level, lowest = (uint64_t) -1, lowest_qgroupid = 0;
+ _cleanup_free_ uint64_t *qgroups = NULL;
+ int r, n, i;
+
+ assert(fd >= 0);
+ assert(ret);
+
+ /* This finds the "subtree" qgroup for a specific
+ * subvolume. This only works for subvolumes that have been
+ * prepared with btrfs_subvol_auto_qgroup_fd() with
+ * insert_intermediary_qgroup=true (or equivalent). For others
+ * it will return the leaf qgroup instead. The two cases may
+ * be distuingished via the return value, which is 1 in case
+ * an appropriate "subtree" qgroup was found, and 0
+ * otherwise. */
+
+ if (subvol_id == 0) {
+ r = btrfs_subvol_get_id_fd(fd, &subvol_id);
+ if (r < 0)
+ return r;
+ }
+
+ r = btrfs_qgroupid_split(subvol_id, &level, NULL);
+ if (r < 0)
+ return r;
+ if (level != 0) /* Input must be a leaf qgroup */
+ return -EINVAL;
+
+ n = btrfs_qgroup_find_parents(fd, subvol_id, &qgroups);
+ if (n < 0)
+ return n;
+
+ for (i = 0; i < n; i++) {
+ uint64_t id;
+
+ r = btrfs_qgroupid_split(qgroups[i], &level, &id);
+ if (r < 0)
+ return r;
+
+ if (id != subvol_id)
+ continue;
+
+ if (lowest == (uint64_t) -1 || level < lowest) {
+ lowest_qgroupid = qgroups[i];
+ lowest = level;
+ }
+ }
+
+ if (lowest == (uint64_t) -1) {
+ /* No suitable higher-level qgroup found, let's return
+ * the leaf qgroup instead, and indicate that with the
+ * return value. */
+
+ *ret = subvol_id;
+ return 0;
+ }
+
+ *ret = lowest_qgroupid;
+ return 1;
+}
+
+int btrfs_subvol_get_subtree_quota_fd(int fd, uint64_t subvol_id, BtrfsQuotaInfo *ret) {
+ uint64_t qgroupid;
+ int r;
+
+ assert(fd >= 0);
+ assert(ret);
+
+ /* This determines the quota data of the qgroup with the
+ * lowest level, that shares the id part with the specified
+ * subvolume. This is useful for determining the quota data
+ * for entire subvolume subtrees, as long as the subtrees have
+ * been set up with btrfs_qgroup_subvol_auto_fd() or in a
+ * compatible way */
+
+ r = btrfs_subvol_find_subtree_qgroup(fd, subvol_id, &qgroupid);
+ if (r < 0)
+ return r;
+
+ return btrfs_qgroup_get_quota_fd(fd, qgroupid, ret);
+}
+
+int btrfs_subvol_get_subtree_quota(const char *path, uint64_t subvol_id, BtrfsQuotaInfo *ret) {
+ _cleanup_close_ int fd = -1;
+
+ fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
+ if (fd < 0)
+ return -errno;
+
+ return btrfs_subvol_get_subtree_quota_fd(fd, subvol_id, ret);
+}
+
int btrfs_defrag_fd(int fd) {
struct stat st;
@@ -679,37 +830,79 @@ int btrfs_quota_enable(const char *path, bool b) {
return btrfs_quota_enable_fd(fd, b);
}
-int btrfs_quota_limit_fd(int fd, uint64_t referenced_max) {
+int btrfs_qgroup_set_limit_fd(int fd, uint64_t qgroupid, 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.max_rfer = referenced_max,
.lim.flags = BTRFS_QGROUP_LIMIT_MAX_RFER,
};
+ unsigned c;
int r;
assert(fd >= 0);
- r = btrfs_is_filesystem(fd);
- if (r < 0)
- return r;
- if (!r)
- return -ENOTTY;
+ if (qgroupid == 0) {
+ r = btrfs_subvol_get_id_fd(fd, &qgroupid);
+ if (r < 0)
+ return r;
+ } else {
+ 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;
+ args.qgroupid = qgroupid;
+
+ for (c = 0;; c++) {
+ if (ioctl(fd, BTRFS_IOC_QGROUP_LIMIT, &args) < 0) {
+
+ if (errno == EBUSY && c < 10) {
+ (void) btrfs_quota_scan_wait(fd);
+ continue;
+ }
+
+ return -errno;
+ }
+
+ break;
+ }
return 0;
}
-int btrfs_quota_limit(const char *path, uint64_t referenced_max) {
+int btrfs_qgroup_set_limit(const char *path, uint64_t qgroupid, 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_qgroup_set_limit_fd(fd, qgroupid, referenced_max);
+}
+
+int btrfs_subvol_set_subtree_quota_limit_fd(int fd, uint64_t subvol_id, uint64_t referenced_max) {
+ uint64_t qgroupid;
+ int r;
+
+ assert(fd >= 0);
+
+ r = btrfs_subvol_find_subtree_qgroup(fd, subvol_id, &qgroupid);
+ if (r < 0)
+ return r;
+
+ return btrfs_qgroup_set_limit_fd(fd, qgroupid, referenced_max);
+}
+
+int btrfs_subvol_set_subtree_quota_limit(const char *path, uint64_t subvol_id, 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);
+ return btrfs_subvol_set_subtree_quota_limit_fd(fd, subvol_id, referenced_max);
}
int btrfs_resize_loopback_fd(int fd, uint64_t new_size, bool grow_only) {
@@ -799,7 +992,192 @@ int btrfs_resize_loopback(const char *p, uint64_t new_size, bool grow_only) {
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) {
+int btrfs_qgroupid_make(uint64_t level, uint64_t id, uint64_t *ret) {
+ assert(ret);
+
+ if (level >= (UINT64_C(1) << (64 - BTRFS_QGROUP_LEVEL_SHIFT)))
+ return -EINVAL;
+
+ if (id >= (UINT64_C(1) << BTRFS_QGROUP_LEVEL_SHIFT))
+ return -EINVAL;
+
+ *ret = (level << BTRFS_QGROUP_LEVEL_SHIFT) | id;
+ return 0;
+}
+
+int btrfs_qgroupid_split(uint64_t qgroupid, uint64_t *level, uint64_t *id) {
+ assert(level || id);
+
+ if (level)
+ *level = qgroupid >> BTRFS_QGROUP_LEVEL_SHIFT;
+
+ if (id)
+ *id = qgroupid & ((UINT64_C(1) << BTRFS_QGROUP_LEVEL_SHIFT) - 1);
+
+ return 0;
+}
+
+static int qgroup_create_or_destroy(int fd, bool b, uint64_t qgroupid) {
+
+ struct btrfs_ioctl_qgroup_create_args args = {
+ .create = b,
+ .qgroupid = qgroupid,
+ };
+ unsigned c;
+ int r;
+
+ r = btrfs_is_filesystem(fd);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENOTTY;
+
+ for (c = 0;; c++) {
+ if (ioctl(fd, BTRFS_IOC_QGROUP_CREATE, &args) < 0) {
+
+ /* If quota is not enabled, we get EINVAL. Turn this into a recognizable error */
+ if (errno == EINVAL)
+ return -ENOPROTOOPT;
+
+ if (errno == EBUSY && c < 10) {
+ (void) btrfs_quota_scan_wait(fd);
+ continue;
+ }
+
+ return -errno;
+ }
+
+ break;
+ }
+
+ return 0;
+}
+
+int btrfs_qgroup_create(int fd, uint64_t qgroupid) {
+ return qgroup_create_or_destroy(fd, true, qgroupid);
+}
+
+int btrfs_qgroup_destroy(int fd, uint64_t qgroupid) {
+ return qgroup_create_or_destroy(fd, false, qgroupid);
+}
+
+int btrfs_qgroup_destroy_recursive(int fd, uint64_t qgroupid) {
+ _cleanup_free_ uint64_t *qgroups = NULL;
+ uint64_t subvol_id;
+ int i, n, r;
+
+ /* Destroys the specified qgroup, but unassigns it from all
+ * its parents first. Also, it recursively destroys all
+ * qgroups it is assgined to that have the same id part of the
+ * qgroupid as the specified group. */
+
+ r = btrfs_qgroupid_split(qgroupid, NULL, &subvol_id);
+ if (r < 0)
+ return r;
+
+ n = btrfs_qgroup_find_parents(fd, qgroupid, &qgroups);
+ if (n < 0)
+ return n;
+
+ for (i = 0; i < n; i++) {
+ uint64_t id;
+
+ r = btrfs_qgroupid_split(qgroups[i], NULL, &id);
+ if (r < 0)
+ return r;
+
+ r = btrfs_qgroup_unassign(fd, qgroupid, qgroups[i]);
+ if (r < 0)
+ return r;
+
+ if (id != subvol_id)
+ continue;
+
+ /* The parent qgroupid shares the same id part with
+ * us? If so, destroy it too. */
+
+ (void) btrfs_qgroup_destroy_recursive(fd, qgroups[i]);
+ }
+
+ return btrfs_qgroup_destroy(fd, qgroupid);
+}
+
+int btrfs_quota_scan_start(int fd) {
+ struct btrfs_ioctl_quota_rescan_args args = {};
+
+ assert(fd >= 0);
+
+ if (ioctl(fd, BTRFS_IOC_QUOTA_RESCAN, &args) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int btrfs_quota_scan_wait(int fd) {
+ assert(fd >= 0);
+
+ if (ioctl(fd, BTRFS_IOC_QUOTA_RESCAN_WAIT) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int btrfs_quota_scan_ongoing(int fd) {
+ struct btrfs_ioctl_quota_rescan_args args = {};
+
+ assert(fd >= 0);
+
+ if (ioctl(fd, BTRFS_IOC_QUOTA_RESCAN_STATUS, &args) < 0)
+ return -errno;
+
+ return !!args.flags;
+}
+
+static int qgroup_assign_or_unassign(int fd, bool b, uint64_t child, uint64_t parent) {
+ struct btrfs_ioctl_qgroup_assign_args args = {
+ .assign = b,
+ .src = child,
+ .dst = parent,
+ };
+ unsigned c;
+ int r;
+
+ r = btrfs_is_filesystem(fd);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENOTTY;
+
+ for (c = 0;; c++) {
+ r = ioctl(fd, BTRFS_IOC_QGROUP_ASSIGN, &args);
+ if (r < 0) {
+ if (errno == EBUSY && c < 10) {
+ (void) btrfs_quota_scan_wait(fd);
+ continue;
+ }
+
+ return -errno;
+ }
+
+ if (r == 0)
+ return 0;
+
+ /* If the return value is > 0, we need to request a rescan */
+
+ (void) btrfs_quota_scan_start(fd);
+ return 1;
+ }
+}
+
+int btrfs_qgroup_assign(int fd, uint64_t child, uint64_t parent) {
+ return qgroup_assign_or_unassign(fd, true, child, parent);
+}
+
+int btrfs_qgroup_unassign(int fd, uint64_t child, uint64_t parent) {
+ return qgroup_assign_or_unassign(fd, false, child, parent);
+}
+
+static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol_id, BtrfsRemoveFlags flags) {
struct btrfs_ioctl_search_args args = {
.key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
@@ -828,16 +1206,6 @@ static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol
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;
@@ -848,6 +1216,19 @@ static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol
return r;
}
+ /* 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) {
+ (void) btrfs_qgroup_destroy_recursive(fd, subvol_id); /* for the leaf subvolumes, the qgroup id is identical to the subvol id */
+ return 0;
+ }
+ if (!(flags & BTRFS_REMOVE_RECURSIVE) || errno != ENOTEMPTY)
+ return -errno;
+
+ /* OK, the subvolume is not empty, let's look for child
+ * subvolumes, and remove them, first */
+
args.key.min_offset = args.key.max_offset = subvol_id;
while (btrfs_ioctl_search_args_compare(&args) <= 0) {
@@ -897,7 +1278,7 @@ static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol
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);
+ r = subvol_remove_children(subvol_fd, p, sh->objectid, flags);
else {
_cleanup_close_ int child_fd = -1;
@@ -909,7 +1290,7 @@ static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol
if (child_fd < 0)
return -errno;
- r = subvol_remove_children(child_fd, p, sh->objectid, recursive);
+ r = subvol_remove_children(child_fd, p, sh->objectid, flags);
}
if (r < 0)
return r;
@@ -925,10 +1306,11 @@ static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol
if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) < 0)
return -errno;
+ (void) btrfs_qgroup_destroy_recursive(fd, subvol_id);
return 0;
}
-int btrfs_subvol_remove(const char *path, bool recursive) {
+int btrfs_subvol_remove(const char *path, BtrfsRemoveFlags flags) {
_cleanup_close_ int fd = -1;
const char *subvolume;
int r;
@@ -943,11 +1325,202 @@ int btrfs_subvol_remove(const char *path, bool recursive) {
if (fd < 0)
return fd;
- return subvol_remove_children(fd, subvolume, 0, recursive);
+ return subvol_remove_children(fd, subvolume, 0, flags);
}
-int btrfs_subvol_remove_fd(int fd, const char *subvolume, bool recursive) {
- return subvol_remove_children(fd, subvolume, 0, recursive);
+int btrfs_subvol_remove_fd(int fd, const char *subvolume, BtrfsRemoveFlags flags) {
+ return subvol_remove_children(fd, subvolume, 0, flags);
+}
+
+int btrfs_qgroup_copy_limits(int fd, uint64_t old_qgroupid, uint64_t new_qgroupid) {
+
+ struct btrfs_ioctl_search_args args = {
+ /* Tree of quota items */
+ .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
+
+ /* The object ID is always 0 */
+ .key.min_objectid = 0,
+ .key.max_objectid = 0,
+
+ /* Look precisely for the quota items */
+ .key.min_type = BTRFS_QGROUP_LIMIT_KEY,
+ .key.max_type = BTRFS_QGROUP_LIMIT_KEY,
+
+ /* For our qgroup */
+ .key.min_offset = old_qgroupid,
+ .key.max_offset = old_qgroupid,
+
+ /* No restrictions on the other components */
+ .key.min_transid = 0,
+ .key.max_transid = (uint64_t) -1,
+ };
+
+ int r;
+
+ r = btrfs_is_filesystem(fd);
+ if (r < 0)
+ return r;
+ if (!r)
+ return -ENOTTY;
+
+ 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) {
+ if (errno == ENOENT) /* quota tree missing: quota is not enabled, hence nothing to copy */
+ break;
+
+ return -errno;
+ }
+
+ if (args.key.nr_items <= 0)
+ break;
+
+ FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
+ const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
+ struct btrfs_ioctl_qgroup_limit_args qargs;
+ unsigned c;
+
+ /* Make sure we start the next search at least from this entry */
+ btrfs_ioctl_search_args_set(&args, sh);
+
+ if (sh->objectid != 0)
+ continue;
+ if (sh->type != BTRFS_QGROUP_LIMIT_KEY)
+ continue;
+ if (sh->offset != old_qgroupid)
+ continue;
+
+ /* We found the entry, now copy things over. */
+
+ qargs = (struct btrfs_ioctl_qgroup_limit_args) {
+ .qgroupid = new_qgroupid,
+
+ .lim.max_rfer = le64toh(qli->max_rfer),
+ .lim.max_excl = le64toh(qli->max_excl),
+ .lim.rsv_rfer = le64toh(qli->rsv_rfer),
+ .lim.rsv_excl = le64toh(qli->rsv_excl),
+
+ .lim.flags = le64toh(qli->flags) & (BTRFS_QGROUP_LIMIT_MAX_RFER|
+ BTRFS_QGROUP_LIMIT_MAX_EXCL|
+ BTRFS_QGROUP_LIMIT_RSV_RFER|
+ BTRFS_QGROUP_LIMIT_RSV_EXCL),
+ };
+
+ for (c = 0;; c++) {
+ if (ioctl(fd, BTRFS_IOC_QGROUP_LIMIT, &qargs) < 0) {
+ if (errno == EBUSY && c < 10) {
+ (void) btrfs_quota_scan_wait(fd);
+ continue;
+ }
+ return -errno;
+ }
+
+ break;
+ }
+
+ return 1;
+ }
+
+ /* Increase search key by one, to read the next item, if we can. */
+ if (!btrfs_ioctl_search_args_inc(&args))
+ break;
+ }
+
+ return 0;
+}
+
+static int copy_quota_hierarchy(int fd, uint64_t old_subvol_id, uint64_t new_subvol_id) {
+ _cleanup_free_ uint64_t *old_qgroups = NULL, *old_parent_qgroups = NULL;
+ bool copy_from_parent = false, insert_intermediary_qgroup = false;
+ int n_old_qgroups, n_old_parent_qgroups, r, i;
+ uint64_t old_parent_id;
+
+ assert(fd >= 0);
+
+ /* Copies a reduced form of quota information from the old to
+ * the new subvolume. */
+
+ n_old_qgroups = btrfs_qgroup_find_parents(fd, old_subvol_id, &old_qgroups);
+ if (n_old_qgroups <= 0) /* Nothing to copy */
+ return n_old_qgroups;
+
+ r = btrfs_subvol_get_parent(fd, old_subvol_id, &old_parent_id);
+ if (r == -ENXIO)
+ /* We have no parent, hence nothing to copy. */
+ n_old_parent_qgroups = 0;
+ else if (r < 0)
+ return r;
+ else {
+ n_old_parent_qgroups = btrfs_qgroup_find_parents(fd, old_parent_id, &old_parent_qgroups);
+ if (n_old_parent_qgroups < 0)
+ return n_old_parent_qgroups;
+ }
+
+ for (i = 0; i < n_old_qgroups; i++) {
+ uint64_t id;
+ int j;
+
+ r = btrfs_qgroupid_split(old_qgroups[i], NULL, &id);
+ if (r < 0)
+ return r;
+
+ if (id == old_subvol_id) {
+ /* The old subvolume was member of a qgroup
+ * that had the same id, but a different level
+ * as it self. Let's set up something similar
+ * in the destination. */
+ insert_intermediary_qgroup = true;
+ break;
+ }
+
+ for (j = 0; j < n_old_parent_qgroups; j++)
+ if (old_parent_qgroups[j] == old_qgroups[i]) {
+ /* The old subvolume shared a common
+ * parent qgroup with its parent
+ * subvolume. Let's set up something
+ * similar in the destination. */
+ copy_from_parent = true;
+ }
+ }
+
+ if (!insert_intermediary_qgroup && !copy_from_parent)
+ return 0;
+
+ return btrfs_subvol_auto_qgroup_fd(fd, new_subvol_id, insert_intermediary_qgroup);
+}
+
+static int copy_subtree_quota_limits(int fd, uint64_t old_subvol, uint64_t new_subvol) {
+ uint64_t old_subtree_qgroup, new_subtree_qgroup;
+ bool changed;
+ int r;
+
+ /* First copy the leaf limits */
+ r = btrfs_qgroup_copy_limits(fd, old_subvol, new_subvol);
+ if (r < 0)
+ return r;
+ changed = r > 0;
+
+ /* Then, try to copy the subtree limits, if there are any. */
+ r = btrfs_subvol_find_subtree_qgroup(fd, old_subvol, &old_subtree_qgroup);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return changed;
+
+ r = btrfs_subvol_find_subtree_qgroup(fd, new_subvol, &new_subtree_qgroup);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return changed;
+
+ r = btrfs_qgroup_copy_limits(fd, old_subtree_qgroup, new_subtree_qgroup);
+ if (r != 0)
+ return r;
+
+ return changed;
}
static int subvol_snapshot_children(int old_fd, int new_fd, const char *subvolume, uint64_t old_subvol_id, BtrfsSnapshotFlags flags) {
@@ -978,12 +1551,12 @@ static int subvol_snapshot_children(int old_fd, int new_fd, const char *subvolum
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))
+ if (!(flags & BTRFS_SNAPSHOT_RECURSIVE) &&
+ !(flags & BTRFS_SNAPSHOT_QUOTA))
return 0;
if (old_subvol_id == 0) {
@@ -996,6 +1569,17 @@ static int subvol_snapshot_children(int old_fd, int new_fd, const char *subvolum
if (r < 0)
return r;
+ if (flags & BTRFS_SNAPSHOT_QUOTA)
+ (void) copy_quota_hierarchy(new_fd, old_subvol_id, new_subvol_id);
+
+ if (!(flags & BTRFS_SNAPSHOT_RECURSIVE)) {
+
+ if (flags & BTRFS_SNAPSHOT_QUOTA)
+ (void) copy_subtree_quota_limits(new_fd, old_subvol_id, new_subvol_id);
+
+ return 0;
+ }
+
args.key.min_offset = args.key.max_offset = old_subvol_id;
while (btrfs_ioctl_search_args_compare(&args) <= 0) {
@@ -1113,6 +1697,9 @@ static int subvol_snapshot_children(int old_fd, int new_fd, const char *subvolum
break;
}
+ if (flags & BTRFS_SNAPSHOT_QUOTA)
+ (void) copy_subtree_quota_limits(new_fd, old_subvol_id, new_subvol_id);
+
return 0;
}
@@ -1124,7 +1711,7 @@ int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlag
assert(old_fd >= 0);
assert(new_path);
- r = btrfs_is_subvol(old_fd);
+ r = btrfs_is_subvol_fd(old_fd);
if (r < 0)
return r;
if (r == 0) {
@@ -1137,14 +1724,14 @@ int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlag
r = copy_directory_fd(old_fd, new_path, true);
if (r < 0) {
- btrfs_subvol_remove(new_path, false);
+ (void) btrfs_subvol_remove(new_path, BTRFS_REMOVE_QUOTA);
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);
+ (void) btrfs_subvol_remove(new_path, BTRFS_REMOVE_QUOTA);
return r;
}
}
@@ -1175,3 +1762,311 @@ int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnaps
return btrfs_subvol_snapshot_fd(old_fd, new_path, flags);
}
+
+int btrfs_qgroup_find_parents(int fd, uint64_t qgroupid, uint64_t **ret) {
+
+ struct btrfs_ioctl_search_args args = {
+ /* Tree of quota items */
+ .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
+
+ /* Look precisely for the quota relation items */
+ .key.min_type = BTRFS_QGROUP_RELATION_KEY,
+ .key.max_type = BTRFS_QGROUP_RELATION_KEY,
+
+ /* No restrictions on the other components */
+ .key.min_offset = 0,
+ .key.max_offset = (uint64_t) -1,
+
+ .key.min_transid = 0,
+ .key.max_transid = (uint64_t) -1,
+ };
+
+ _cleanup_free_ uint64_t *items = NULL;
+ size_t n_items = 0, n_allocated = 0;
+ int r;
+
+ assert(fd >= 0);
+ assert(ret);
+
+ if (qgroupid == 0) {
+ r = btrfs_subvol_get_id_fd(fd, &qgroupid);
+ if (r < 0)
+ return r;
+ } else {
+ r = btrfs_is_filesystem(fd);
+ if (r < 0)
+ return r;
+ if (!r)
+ return -ENOTTY;
+ }
+
+ args.key.min_objectid = args.key.max_objectid = qgroupid;
+
+ 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) {
+ if (errno == ENOENT) /* quota tree missing: quota is disabled */
+ break;
+
+ return -errno;
+ }
+
+ if (args.key.nr_items <= 0)
+ break;
+
+ FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
+
+ /* Make sure we start the next search at least from this entry */
+ btrfs_ioctl_search_args_set(&args, sh);
+
+ if (sh->type != BTRFS_QGROUP_RELATION_KEY)
+ continue;
+ if (sh->offset < sh->objectid)
+ continue;
+ if (sh->objectid != qgroupid)
+ continue;
+
+ if (!GREEDY_REALLOC(items, n_allocated, n_items+1))
+ return -ENOMEM;
+
+ items[n_items++] = sh->offset;
+ }
+
+ /* Increase search key by one, to read the next item, if we can. */
+ if (!btrfs_ioctl_search_args_inc(&args))
+ break;
+ }
+
+ if (n_items <= 0) {
+ *ret = NULL;
+ return 0;
+ }
+
+ *ret = items;
+ items = NULL;
+
+ return (int) n_items;
+}
+
+int btrfs_subvol_auto_qgroup_fd(int fd, uint64_t subvol_id, bool insert_intermediary_qgroup) {
+ _cleanup_free_ uint64_t *qgroups = NULL;
+ uint64_t parent_subvol;
+ bool changed = false;
+ int n = 0, r;
+
+ assert(fd >= 0);
+
+ /*
+ * Sets up the specified subvolume's qgroup automatically in
+ * one of two ways:
+ *
+ * If insert_intermediary_qgroup is false, the subvolume's
+ * leaf qgroup will be assigned to the same parent qgroups as
+ * the subvolume's parent subvolume.
+ *
+ * If insert_intermediary_qgroup is true a new intermediary
+ * higher-level qgroup is created, with a higher level number,
+ * but reusing the id of the subvolume. The level number is
+ * picked as one smaller than the lowest level qgroup the
+ * parent subvolume is a member of. If the parent subvolume's
+ * leaf qgroup is assigned to no higher-level qgroup a new
+ * qgroup of level 255 is created instead. Either way, the new
+ * qgroup is then assigned to the parent's higher-level
+ * qgroup, and the subvolume itself is assigned to it.
+ *
+ * If the subvolume is already assigned to a higher level
+ * qgroup, no operation is executed.
+ *
+ * Effectively this means: regardless if
+ * insert_intermediary_qgroup is true or not, after this
+ * function is invoked the subvolume will be accounted within
+ * the same qgroups as the parent. However, if it is true, it
+ * will also get its own higher-level qgroup, which may in
+ * turn be used by subvolumes created beneath this subvolume
+ * later on.
+ *
+ * This hence defines a simple default qgroup setup for
+ * subvolumes, as long as this function is invoked on each
+ * created subvolume: each subvolume is always accounting
+ * together with its immediate parents. Optionally, if
+ * insert_intermediary_qgroup is true, it will also get a
+ * qgroup that then includes all its own child subvolumes.
+ */
+
+ if (subvol_id == 0) {
+ r = btrfs_is_subvol_fd(fd);
+ if (r < 0)
+ return r;
+ if (!r)
+ return -ENOTTY;
+
+ r = btrfs_subvol_get_id_fd(fd, &subvol_id);
+ if (r < 0)
+ return r;
+ }
+
+ n = btrfs_qgroup_find_parents(fd, subvol_id, &qgroups);
+ if (n < 0)
+ return n;
+ if (n > 0) /* already parent qgroups set up, let's bail */
+ return 0;
+
+ qgroups = mfree(qgroups);
+
+ r = btrfs_subvol_get_parent(fd, subvol_id, &parent_subvol);
+ if (r == -ENXIO)
+ /* No parent, hence no qgroup memberships */
+ n = 0;
+ else if (r < 0)
+ return r;
+ else {
+ n = btrfs_qgroup_find_parents(fd, parent_subvol, &qgroups);
+ if (n < 0)
+ return n;
+ }
+
+ if (insert_intermediary_qgroup) {
+ uint64_t lowest = 256, new_qgroupid;
+ bool created = false;
+ int i;
+
+ /* Determine the lowest qgroup that the parent
+ * subvolume is assigned to. */
+
+ for (i = 0; i < n; i++) {
+ uint64_t level;
+
+ r = btrfs_qgroupid_split(qgroups[i], &level, NULL);
+ if (r < 0)
+ return r;
+
+ if (level < lowest)
+ lowest = level;
+ }
+
+ if (lowest <= 1) /* There are no levels left we could use insert an intermediary qgroup at */
+ return -EBUSY;
+
+ r = btrfs_qgroupid_make(lowest - 1, subvol_id, &new_qgroupid);
+ if (r < 0)
+ return r;
+
+ /* Create the new intermediary group, unless it already exists */
+ r = btrfs_qgroup_create(fd, new_qgroupid);
+ if (r < 0 && r != -EEXIST)
+ return r;
+ if (r >= 0)
+ changed = created = true;
+
+ for (i = 0; i < n; i++) {
+ r = btrfs_qgroup_assign(fd, new_qgroupid, qgroups[i]);
+ if (r < 0 && r != -EEXIST) {
+ if (created)
+ (void) btrfs_qgroup_destroy_recursive(fd, new_qgroupid);
+
+ return r;
+ }
+ if (r >= 0)
+ changed = true;
+ }
+
+ r = btrfs_qgroup_assign(fd, subvol_id, new_qgroupid);
+ if (r < 0 && r != -EEXIST) {
+ if (created)
+ (void) btrfs_qgroup_destroy_recursive(fd, new_qgroupid);
+ return r;
+ }
+ if (r >= 0)
+ changed = true;
+
+ } else {
+ int i;
+
+ /* Assign our subvolume to all the same qgroups as the parent */
+
+ for (i = 0; i < n; i++) {
+ r = btrfs_qgroup_assign(fd, subvol_id, qgroups[i]);
+ if (r < 0 && r != -EEXIST)
+ return r;
+ if (r >= 0)
+ changed = true;
+ }
+ }
+
+ return changed;
+}
+
+int btrfs_subvol_auto_qgroup(const char *path, uint64_t subvol_id, bool create_intermediary_qgroup) {
+ _cleanup_close_ int fd = -1;
+
+ fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ if (fd < 0)
+ return -errno;
+
+ return btrfs_subvol_auto_qgroup_fd(fd, subvol_id, create_intermediary_qgroup);
+}
+
+int btrfs_subvol_get_parent(int fd, uint64_t subvol_id, uint64_t *ret) {
+
+ struct btrfs_ioctl_search_args args = {
+ /* Tree of tree roots */
+ .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
+
+ /* Look precisely for the subvolume items */
+ .key.min_type = BTRFS_ROOT_BACKREF_KEY,
+ .key.max_type = BTRFS_ROOT_BACKREF_KEY,
+
+ /* No restrictions on the other components */
+ .key.min_offset = 0,
+ .key.max_offset = (uint64_t) -1,
+
+ .key.min_transid = 0,
+ .key.max_transid = (uint64_t) -1,
+ };
+ int r;
+
+ assert(fd >= 0);
+ assert(ret);
+
+ if (subvol_id == 0) {
+ r = btrfs_subvol_get_id_fd(fd, &subvol_id);
+ if (r < 0)
+ return r;
+ } else {
+ r = btrfs_is_filesystem(fd);
+ if (r < 0)
+ return r;
+ if (!r)
+ return -ENOTTY;
+ }
+
+ args.key.min_objectid = args.key.max_objectid = 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 negative_errno();
+
+ if (args.key.nr_items <= 0)
+ break;
+
+ FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
+
+ if (sh->type != BTRFS_ROOT_BACKREF_KEY)
+ continue;
+ if (sh->objectid != subvol_id)
+ continue;
+
+ *ret = sh->offset;
+ return 0;
+ }
+ }
+
+ return -ENXIO;
+}
diff --git a/src/basic/btrfs-util.h b/src/basic/btrfs-util.h
index 8632c3638c..31b9c02785 100644
--- a/src/basic/btrfs-util.h
+++ b/src/basic/btrfs-util.h
@@ -22,8 +22,11 @@
#pragma once
#include <stdbool.h>
+#include <stdint.h>
#include <sys/types.h>
+#include "sd-id128.h"
+
#include "time-util.h"
typedef struct BtrfsSubvolInfo {
@@ -47,10 +50,37 @@ typedef enum BtrfsSnapshotFlags {
BTRFS_SNAPSHOT_FALLBACK_COPY = 1,
BTRFS_SNAPSHOT_READ_ONLY = 2,
BTRFS_SNAPSHOT_RECURSIVE = 4,
+ BTRFS_SNAPSHOT_QUOTA = 8,
} BtrfsSnapshotFlags;
+typedef enum BtrfsRemoveFlags {
+ BTRFS_REMOVE_RECURSIVE = 1,
+ BTRFS_REMOVE_QUOTA = 2,
+} BtrfsRemoveFlags;
+
int btrfs_is_filesystem(int fd);
-int btrfs_is_subvol(int fd);
+
+int btrfs_is_subvol_fd(int fd);
+int btrfs_is_subvol(const char *path);
+
+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_scan_start(int fd);
+int btrfs_quota_scan_wait(int fd);
+int btrfs_quota_scan_ongoing(int fd);
+
+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_make(const char *path);
int btrfs_subvol_make_label(const char *path);
@@ -58,31 +88,46 @@ int btrfs_subvol_make_label(const char *path);
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_remove(const char *path, BtrfsRemoveFlags flags);
+int btrfs_subvol_remove_fd(int fd, const char *subvolume, BtrfsRemoveFlags flags);
+
int btrfs_subvol_set_read_only_fd(int fd, bool b);
int btrfs_subvol_set_read_only(const char *path, bool b);
int btrfs_subvol_get_read_only_fd(int fd);
+
int btrfs_subvol_get_id(int fd, const char *subvolume, uint64_t *ret);
int btrfs_subvol_get_id_fd(int fd, uint64_t *ret);
-int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *info);
-int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *quota);
+int btrfs_subvol_get_parent(int fd, uint64_t subvol_id, uint64_t *ret);
-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_subvol_get_info_fd(int fd, uint64_t subvol_id, BtrfsSubvolInfo *info);
-int btrfs_get_block_device_fd(int fd, dev_t *dev);
-int btrfs_get_block_device(const char *path, dev_t *dev);
+int btrfs_subvol_find_subtree_qgroup(int fd, uint64_t subvol_id, uint64_t *ret);
-int btrfs_defrag_fd(int fd);
-int btrfs_defrag(const char *p);
+int btrfs_subvol_get_subtree_quota(const char *path, uint64_t subvol_id, BtrfsQuotaInfo *quota);
+int btrfs_subvol_get_subtree_quota_fd(int fd, uint64_t subvol_id, BtrfsQuotaInfo *quota);
-int btrfs_quota_enable_fd(int fd, bool b);
-int btrfs_quota_enable(const char *path, bool b);
+int btrfs_subvol_set_subtree_quota_limit(const char *path, uint64_t subvol_id, uint64_t referenced_max);
+int btrfs_subvol_set_subtree_quota_limit_fd(int fd, uint64_t subvol_id, uint64_t referenced_max);
-int btrfs_quota_limit_fd(int fd, uint64_t referenced_max);
-int btrfs_quota_limit(const char *path, uint64_t referenced_max);
+int btrfs_subvol_auto_qgroup_fd(int fd, uint64_t subvol_id, bool new_qgroup);
+int btrfs_subvol_auto_qgroup(const char *path, uint64_t subvol_id, bool create_intermediary_qgroup);
-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_qgroupid_make(uint64_t level, uint64_t id, uint64_t *ret);
+int btrfs_qgroupid_split(uint64_t qgroupid, uint64_t *level, uint64_t *id);
+
+int btrfs_qgroup_create(int fd, uint64_t qgroupid);
+int btrfs_qgroup_destroy(int fd, uint64_t qgroupid);
+int btrfs_qgroup_destroy_recursive(int fd, uint64_t qgroupid);
+
+int btrfs_qgroup_set_limit_fd(int fd, uint64_t qgroupid, uint64_t referenced_max);
+int btrfs_qgroup_set_limit(const char *path, uint64_t qgroupid, uint64_t referenced_max);
+
+int btrfs_qgroup_copy_limits(int fd, uint64_t old_qgroupid, uint64_t new_qgroupid);
+
+int btrfs_qgroup_assign(int fd, uint64_t child, uint64_t parent);
+int btrfs_qgroup_unassign(int fd, uint64_t child, uint64_t parent);
+
+int btrfs_qgroup_find_parents(int fd, uint64_t qgroupid, uint64_t **ret);
-int btrfs_subvol_remove(const char *path, bool recursive);
-int btrfs_subvol_remove_fd(int fd, const char *subvolume, bool recursive);
+int btrfs_qgroup_get_quota_fd(int fd, uint64_t qgroupid, BtrfsQuotaInfo *quota);
+int btrfs_qgroup_get_quota(const char *path, uint64_t qgroupid, BtrfsQuotaInfo *quota);
diff --git a/src/basic/bus-label.c b/src/basic/bus-label.c
index ccc9f2bf8e..d8d5863b03 100644
--- a/src/basic/bus-label.c
+++ b/src/basic/bus-label.c
@@ -21,10 +21,10 @@
#include <stdlib.h>
-#include "util.h"
-#include "macro.h"
-
+#include "alloc-util.h"
#include "bus-label.h"
+#include "hexdecoct.h"
+#include "macro.h"
char *bus_label_escape(const char *s) {
char *r, *t;
diff --git a/src/basic/bus-label.h b/src/basic/bus-label.h
index ed1dc4e0a7..f51153ce6d 100644
--- a/src/basic/bus-label.h
+++ b/src/basic/bus-label.h
@@ -21,6 +21,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <stddef.h>
#include <stdlib.h>
#include <string.h>
diff --git a/src/basic/c-rbtree.c b/src/basic/c-rbtree.c
new file mode 100644
index 0000000000..914d7e5229
--- /dev/null
+++ b/src/basic/c-rbtree.c
@@ -0,0 +1,679 @@
+/***
+ This file is part of systemd. See COPYING for details.
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General 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/>.
+***/
+
+/*
+ * RB-Tree Implementation
+ * This implements the insertion/removal of elements in RB-Trees. You're highly
+ * recommended to have an RB-Tree documentation at hand when reading this. Both
+ * insertion and removal can be split into a handful of situations that can
+ * occur. Those situations are enumerated as "Case 1" to "Case n" here, and
+ * follow closely the cases described in most RB-Tree documentations. This file
+ * does not explain why it is enough to handle just those cases, nor does it
+ * provide a proof of correctness. Dig out your algorithm 101 handbook if
+ * you're interested.
+ *
+ * This implementation is *not* straightforward. Usually, a handful of
+ * rotation, reparent, swap and link helpers can be used to implement the
+ * rebalance operations. However, those often perform unnecessary writes.
+ * Therefore, this implementation hard-codes all the operations. You're highly
+ * recommended to look at the two basic helpers before reading the code:
+ * c_rbtree_swap_child()
+ * c_rbtree_set_parent_and_color()
+ * Those are the only helpers used, hence, you should really know what they do
+ * before digging into the code.
+ *
+ * For a highlevel documentation of the API, see the header file and docbook
+ * comments.
+ */
+
+#include <assert.h>
+#include <stddef.h>
+#include "c-rbtree.h"
+
+enum {
+ C_RBNODE_RED = 0,
+ C_RBNODE_BLACK = 1,
+};
+
+static inline unsigned long c_rbnode_color(CRBNode *n) {
+ return (unsigned long)n->__parent_and_color & 1UL;
+}
+
+static inline _Bool c_rbnode_is_red(CRBNode *n) {
+ return c_rbnode_color(n) == C_RBNODE_RED;
+}
+
+static inline _Bool c_rbnode_is_black(CRBNode *n) {
+ return c_rbnode_color(n) == C_RBNODE_BLACK;
+}
+
+/**
+ * c_rbnode_leftmost() - return leftmost child
+ * @n: current node, or NULL
+ *
+ * This returns the leftmost child of @n. If @n is NULL, this will return NULL.
+ * In all other cases, this function returns a valid pointer. That is, if @n
+ * does not have any left children, this returns @n.
+ *
+ * Worst case runtime (n: number of elements in tree): O(log(n))
+ *
+ * Return: Pointer to leftmost child, or NULL.
+ */
+CRBNode *c_rbnode_leftmost(CRBNode *n) {
+ if (n)
+ while (n->left)
+ n = n->left;
+ return n;
+}
+
+/**
+ * c_rbnode_rightmost() - return rightmost child
+ * @n: current node, or NULL
+ *
+ * This returns the rightmost child of @n. If @n is NULL, this will return
+ * NULL. In all other cases, this function returns a valid pointer. That is, if
+ * @n does not have any right children, this returns @n.
+ *
+ * Worst case runtime (n: number of elements in tree): O(log(n))
+ *
+ * Return: Pointer to rightmost child, or NULL.
+ */
+CRBNode *c_rbnode_rightmost(CRBNode *n) {
+ if (n)
+ while (n->right)
+ n = n->right;
+ return n;
+}
+
+/**
+ * c_rbnode_next() - return next node
+ * @n: current node, or NULL
+ *
+ * An RB-Tree always defines a linear order of its elements. This function
+ * returns the logically next node to @n. If @n is NULL, the last node or
+ * unlinked, this returns NULL.
+ *
+ * Worst case runtime (n: number of elements in tree): O(log(n))
+ *
+ * Return: Pointer to next node, or NULL.
+ */
+CRBNode *c_rbnode_next(CRBNode *n) {
+ CRBNode *p;
+
+ if (!c_rbnode_is_linked(n))
+ return NULL;
+ if (n->right)
+ return c_rbnode_leftmost(n->right);
+
+ while ((p = c_rbnode_parent(n)) && n == p->right)
+ n = p;
+
+ return p;
+}
+
+/**
+ * c_rbnode_prev() - return previous node
+ * @n: current node, or NULL
+ *
+ * An RB-Tree always defines a linear order of its elements. This function
+ * returns the logically previous node to @n. If @n is NULL, the first node or
+ * unlinked, this returns NULL.
+ *
+ * Worst case runtime (n: number of elements in tree): O(log(n))
+ *
+ * Return: Pointer to previous node, or NULL.
+ */
+CRBNode *c_rbnode_prev(CRBNode *n) {
+ CRBNode *p;
+
+ if (!c_rbnode_is_linked(n))
+ return NULL;
+ if (n->left)
+ return c_rbnode_rightmost(n->left);
+
+ while ((p = c_rbnode_parent(n)) && n == p->left)
+ n = p;
+
+ return p;
+}
+
+/**
+ * c_rbtree_first() - return first node
+ * @t: tree to operate on
+ *
+ * An RB-Tree always defines a linear order of its elements. This function
+ * returns the logically first node in @t. If @t is empty, NULL is returned.
+ *
+ * Fixed runtime (n: number of elements in tree): O(log(n))
+ *
+ * Return: Pointer to first node, or NULL.
+ */
+CRBNode *c_rbtree_first(CRBTree *t) {
+ assert(t);
+ return c_rbnode_leftmost(t->root);
+}
+
+/**
+ * c_rbtree_last() - return last node
+ * @t: tree to operate on
+ *
+ * An RB-Tree always defines a linear order of its elements. This function
+ * returns the logically last node in @t. If @t is empty, NULL is returned.
+ *
+ * Fixed runtime (n: number of elements in tree): O(log(n))
+ *
+ * Return: Pointer to last node, or NULL.
+ */
+CRBNode *c_rbtree_last(CRBTree *t) {
+ assert(t);
+ return c_rbnode_rightmost(t->root);
+}
+
+/*
+ * Set the color and parent of a node. This should be treated as a simple
+ * assignment of the 'color' and 'parent' fields of the node. No other magic is
+ * applied. But since both fields share its backing memory, this helper
+ * function is provided.
+ */
+static inline void c_rbnode_set_parent_and_color(CRBNode *n, CRBNode *p, unsigned long c) {
+ assert(!((unsigned long)p & 1));
+ assert(c < 2);
+ n->__parent_and_color = (CRBNode*)((unsigned long)p | c);
+}
+
+/* same as c_rbnode_set_parent_and_color(), but keeps the current parent */
+static inline void c_rbnode_set_color(CRBNode *n, unsigned long c) {
+ c_rbnode_set_parent_and_color(n, c_rbnode_parent(n), c);
+}
+
+/* same as c_rbnode_set_parent_and_color(), but keeps the current color */
+static inline void c_rbnode_set_parent(CRBNode *n, CRBNode *p) {
+ c_rbnode_set_parent_and_color(n, p, c_rbnode_color(n));
+}
+
+/*
+ * This function partially replaces an existing child pointer to a new one. The
+ * existing child must be given as @old, the new child as @new. @p must be the
+ * parent of @old (or NULL if it has no parent).
+ * This function ensures that the parent of @old now points to @new. However,
+ * it does *NOT* change the parent pointer of @new. The caller must ensure
+ * this.
+ * If @p is NULL, this function ensures that the root-pointer is adjusted
+ * instead (given as @t).
+ */
+static inline void c_rbtree_swap_child(CRBTree *t, CRBNode *p, CRBNode *old, CRBNode *new) {
+ if (p) {
+ if (p->left == old)
+ p->left = new;
+ else
+ p->right = new;
+ } else {
+ t->root = new;
+ }
+}
+
+static inline CRBNode *c_rbtree_paint_one(CRBTree *t, CRBNode *n) {
+ CRBNode *p, *g, *gg, *u, *x;
+
+ /*
+ * Paint a single node according to RB-Tree rules. The node must
+ * already be linked into the tree and painted red.
+ * We repaint the node or rotate the tree, if required. In case a
+ * recursive repaint is required, the next node to be re-painted
+ * is returned.
+ * p: parent
+ * g: grandparent
+ * gg: grandgrandparent
+ * u: uncle
+ * x: temporary
+ */
+
+ /* node is red, so we can access the parent directly */
+ p = n->__parent_and_color;
+
+ if (!p) {
+ /* Case 1:
+ * We reached the root. Mark it black and be done. As all
+ * leaf-paths share the root, the ratio of black nodes on each
+ * path stays the same. */
+ c_rbnode_set_parent_and_color(n, p, C_RBNODE_BLACK);
+ n = NULL;
+ } else if (c_rbnode_is_black(p)) {
+ /* Case 2:
+ * The parent is already black. As our node is red, we did not
+ * change the number of black nodes on any path, nor do we have
+ * multiple consecutive red nodes. */
+ n = NULL;
+ } else if (p == p->__parent_and_color->left) { /* parent is red, so grandparent exists */
+ g = p->__parent_and_color;
+ gg = c_rbnode_parent(g);
+ u = g->right;
+
+ if (u && c_rbnode_is_red(u)) {
+ /* Case 3:
+ * Parent and uncle are both red. We know the
+ * grandparent must be black then. Repaint parent and
+ * uncle black, the grandparent red and recurse into
+ * the grandparent. */
+ c_rbnode_set_parent_and_color(p, g, C_RBNODE_BLACK);
+ c_rbnode_set_parent_and_color(u, g, C_RBNODE_BLACK);
+ c_rbnode_set_parent_and_color(g, gg, C_RBNODE_RED);
+ n = g;
+ } else {
+ /* parent is red, uncle is black */
+
+ if (n == p->right) {
+ /* Case 4:
+ * We're the right child. Rotate on parent to
+ * become left child, so we can handle it the
+ * same as case 5. */
+ x = n->left;
+ p->right = n->left;
+ n->left = p;
+ if (x)
+ c_rbnode_set_parent_and_color(x, p, C_RBNODE_BLACK);
+ c_rbnode_set_parent_and_color(p, n, C_RBNODE_RED);
+ p = n;
+ }
+
+ /* 'n' is invalid from here on! */
+ n = NULL;
+
+ /* Case 5:
+ * We're the red left child or a red parent, black
+ * grandparent and uncle. Rotate on grandparent and
+ * switch color with parent. Number of black nodes on
+ * each path stays the same, but we got rid of the
+ * double red path. As the grandparent is still black,
+ * we're done. */
+ x = p->right;
+ g->left = x;
+ p->right = g;
+ if (x)
+ c_rbnode_set_parent_and_color(x, g, C_RBNODE_BLACK);
+ c_rbnode_set_parent_and_color(p, gg, C_RBNODE_BLACK);
+ c_rbnode_set_parent_and_color(g, p, C_RBNODE_RED);
+ c_rbtree_swap_child(t, gg, g, p);
+ }
+ } else /* if (p == p->__parent_and_color->left) */ { /* same as above, but mirrored */
+ g = p->__parent_and_color;
+ gg = c_rbnode_parent(g);
+ u = g->left;
+
+ if (u && c_rbnode_is_red(u)) {
+ c_rbnode_set_parent_and_color(p, g, C_RBNODE_BLACK);
+ c_rbnode_set_parent_and_color(u, g, C_RBNODE_BLACK);
+ c_rbnode_set_parent_and_color(g, gg, C_RBNODE_RED);
+ n = g;
+ } else {
+ if (n == p->left) {
+ x = n->right;
+ p->left = n->right;
+ n->right = p;
+ if (x)
+ c_rbnode_set_parent_and_color(x, p, C_RBNODE_BLACK);
+ c_rbnode_set_parent_and_color(p, n, C_RBNODE_RED);
+ p = n;
+ }
+
+ n = NULL;
+
+ x = p->left;
+ g->right = x;
+ p->left = g;
+ if (x)
+ c_rbnode_set_parent_and_color(x, g, C_RBNODE_BLACK);
+ c_rbnode_set_parent_and_color(p, gg, C_RBNODE_BLACK);
+ c_rbnode_set_parent_and_color(g, p, C_RBNODE_RED);
+ c_rbtree_swap_child(t, gg, g, p);
+ }
+ }
+
+ return n;
+}
+
+static inline void c_rbtree_paint(CRBTree *t, CRBNode *n) {
+ assert(t);
+ assert(n);
+
+ while (n)
+ n = c_rbtree_paint_one(t, n);
+}
+
+/**
+ * c_rbtree_add() - add node to tree
+ * @t: tree to operate one
+ * @p: parent node to link under, or NULL
+ * @l: left/right slot of @p (or root) to link at
+ * @n: node to add
+ *
+ * This links @n into the tree given as @t. The caller must provide the exact
+ * spot where to link the node. That is, the caller must traverse the tree
+ * based on their search order. Once they hit a leaf where to insert the node,
+ * call this function to link it and rebalance the tree.
+ *
+ * A typical insertion would look like this (@t is your tree, @n is your node):
+ *
+ * CRBNode **i, *p;
+ *
+ * i = &t->root;
+ * p = NULL;
+ * while (*i) {
+ * p = *i;
+ * if (compare(n, *i) < 0)
+ * i = &(*i)->left;
+ * else
+ * i = &(*i)->right;
+ * }
+ *
+ * c_rbtree_add(t, p, i, n);
+ *
+ * Once the node is linked into the tree, a simple lookup on the same tree can
+ * be coded like this:
+ *
+ * CRBNode *i;
+ *
+ * i = t->root;
+ * while (i) {
+ * int v = compare(n, i);
+ * if (v < 0)
+ * i = (*i)->left;
+ * else if (v > 0)
+ * i = (*i)->right;
+ * else
+ * break;
+ * }
+ *
+ * When you add nodes to a tree, the memory contents of the node do not matter.
+ * That is, there is no need to initialize the node via c_rbnode_init().
+ * However, if you relink nodes multiple times during their lifetime, it is
+ * usually very convenient to use c_rbnode_init() and c_rbtree_remove_init().
+ * In those cases, you should validate that a node is unlinked before you call
+ * c_rbtree_add().
+ */
+void c_rbtree_add(CRBTree *t, CRBNode *p, CRBNode **l, CRBNode *n) {
+ assert(t);
+ assert(l);
+ assert(n);
+ assert(!p || l == &p->left || l == &p->right);
+ assert(p || l == &t->root);
+
+ c_rbnode_set_parent_and_color(n, p, C_RBNODE_RED);
+ n->left = n->right = NULL;
+ *l = n;
+
+ c_rbtree_paint(t, n);
+}
+
+static inline CRBNode *c_rbtree_rebalance_one(CRBTree *t, CRBNode *p, CRBNode *n) {
+ CRBNode *s, *x, *y, *g;
+
+ /*
+ * Rebalance tree after a node was removed. This happens only if you
+ * remove a black node and one path is now left with an unbalanced
+ * number or black nodes.
+ * This function assumes all paths through p and n have one black node
+ * less than all other paths. If recursive fixup is required, the
+ * current node is returned.
+ */
+
+ if (n == p->left) {
+ s = p->right;
+ if (c_rbnode_is_red(s)) {
+ /* Case 3:
+ * We have a red node as sibling. Rotate it onto our
+ * side so we can later on turn it black. This way, we
+ * gain the additional black node in our path. */
+ g = c_rbnode_parent(p);
+ x = s->left;
+ p->right = x;
+ s->left = p;
+ c_rbnode_set_parent_and_color(x, p, C_RBNODE_BLACK);
+ c_rbnode_set_parent_and_color(s, g, c_rbnode_color(p));
+ c_rbnode_set_parent_and_color(p, s, C_RBNODE_RED);
+ c_rbtree_swap_child(t, g, p, s);
+ s = x;
+ }
+
+ x = s->right;
+ if (!x || c_rbnode_is_black(x)) {
+ y = s->left;
+ if (!y || c_rbnode_is_black(y)) {
+ /* Case 4:
+ * Our sibling is black and has only black
+ * children. Flip it red and turn parent black.
+ * This way we gained a black node in our path,
+ * or we fix it recursively one layer up, which
+ * will rotate the red sibling as parent. */
+ c_rbnode_set_parent_and_color(s, p, C_RBNODE_RED);
+ if (c_rbnode_is_black(p))
+ return p;
+
+ c_rbnode_set_parent_and_color(p, c_rbnode_parent(p), C_RBNODE_BLACK);
+ return NULL;
+ }
+
+ /* Case 5:
+ * Left child of our sibling is red, right one is black.
+ * Rotate on parent so the right child of our sibling is
+ * now red, and we can fall through to case 6. */
+ x = y->right;
+ s->left = y->right;
+ y->right = s;
+ p->right = y;
+ if (x)
+ c_rbnode_set_parent_and_color(x, s, C_RBNODE_BLACK);
+ x = s;
+ s = y;
+ }
+
+ /* Case 6:
+ * The right child of our sibling is red. Rotate left and flip
+ * colors, which gains us an additional black node in our path,
+ * that was previously on our sibling. */
+ g = c_rbnode_parent(p);
+ y = s->left;
+ p->right = y;
+ s->left = p;
+ c_rbnode_set_parent_and_color(x, s, C_RBNODE_BLACK);
+ if (y)
+ c_rbnode_set_parent_and_color(y, p, c_rbnode_color(y));
+ c_rbnode_set_parent_and_color(s, g, c_rbnode_color(p));
+ c_rbnode_set_parent_and_color(p, s, C_RBNODE_BLACK);
+ c_rbtree_swap_child(t, g, p, s);
+ } else /* if (!n || n == p->right) */ { /* same as above, but mirrored */
+ s = p->left;
+ if (c_rbnode_is_red(s)) {
+ g = c_rbnode_parent(p);
+ x = s->right;
+ p->left = x;
+ s->right = p;
+ c_rbnode_set_parent_and_color(x, p, C_RBNODE_BLACK);
+ c_rbnode_set_parent_and_color(s, g, C_RBNODE_BLACK);
+ c_rbnode_set_parent_and_color(p, s, C_RBNODE_RED);
+ c_rbtree_swap_child(t, g, p, s);
+ s = x;
+ }
+
+ x = s->left;
+ if (!x || c_rbnode_is_black(x)) {
+ y = s->right;
+ if (!y || c_rbnode_is_black(y)) {
+ c_rbnode_set_parent_and_color(s, p, C_RBNODE_RED);
+ if (c_rbnode_is_black(p))
+ return p;
+
+ c_rbnode_set_parent_and_color(p, c_rbnode_parent(p), C_RBNODE_BLACK);
+ return NULL;
+ }
+
+ x = y->left;
+ s->right = y->left;
+ y->left = s;
+ p->left = y;
+ if (x)
+ c_rbnode_set_parent_and_color(x, s, C_RBNODE_BLACK);
+ x = s;
+ s = y;
+ }
+
+ g = c_rbnode_parent(p);
+ y = s->right;
+ p->left = y;
+ s->right = p;
+ c_rbnode_set_parent_and_color(x, s, C_RBNODE_BLACK);
+ if (y)
+ c_rbnode_set_parent_and_color(y, p, c_rbnode_color(y));
+ c_rbnode_set_parent_and_color(s, g, c_rbnode_color(p));
+ c_rbnode_set_parent_and_color(p, s, C_RBNODE_BLACK);
+ c_rbtree_swap_child(t, g, p, s);
+ }
+
+ return NULL;
+}
+
+static inline void c_rbtree_rebalance(CRBTree *t, CRBNode *p) {
+ CRBNode *n = NULL;
+
+ assert(t);
+ assert(p);
+
+ do {
+ n = c_rbtree_rebalance_one(t, p, n);
+ p = n ? c_rbnode_parent(n) : NULL;
+ } while (p);
+}
+
+/**
+ * c_rbtree_remove() - remove node from tree
+ * @t: tree to operate one
+ * @n: node to remove
+ *
+ * This removes the given node from its tree. Once unlinked, the tree is
+ * rebalanced.
+ * The caller *must* ensure that the given tree is actually the tree it is
+ * linked on. Otherwise, behavior is undefined.
+ *
+ * This does *NOT* reset @n to being unlinked (for performance reason, this
+ * function *never* modifies @n at all). If you need this, use
+ * c_rbtree_remove_init().
+ */
+void c_rbtree_remove(CRBTree *t, CRBNode *n) {
+ CRBNode *p, *s, *gc, *x, *next = NULL;
+ unsigned long c;
+
+ assert(t);
+ assert(n);
+ assert(c_rbnode_is_linked(n));
+
+ /*
+ * There are three distinct cases during node removal of a tree:
+ * * The node has no children, in which case it can simply be removed.
+ * * The node has exactly one child, in which case the child displaces
+ * its parent.
+ * * The node has two children, in which case there is guaranteed to
+ * be a successor to the node (successor being the node ordered
+ * directly after it). This successor cannot have two children by
+ * itself (two interior nodes can never be successive). Therefore,
+ * we can simply swap the node with its successor (including color)
+ * and have reduced this case to either of the first two.
+ *
+ * Whenever the node we removed was black, we have to rebalance the
+ * tree. Note that this affects the actual node we _remove_, not @n (in
+ * case we swap it).
+ *
+ * p: parent
+ * s: successor
+ * gc: grand-...-child
+ * x: temporary
+ * next: next node to rebalance on
+ */
+
+ if (!n->left) {
+ /*
+ * Case 1:
+ * The node has no left child. If it neither has a right child,
+ * it is a leaf-node and we can simply unlink it. If it also
+ * was black, we have to rebalance, as always if we remove a
+ * black node.
+ * But if the node has a right child, the child *must* be red
+ * (otherwise, the right path has more black nodes as the
+ * non-existing left path), and the node to be removed must
+ * hence be black. We simply replace the node with its child,
+ * turning the red child black, and thus no rebalancing is
+ * required.
+ */
+ p = c_rbnode_parent(n);
+ c = c_rbnode_color(n);
+ c_rbtree_swap_child(t, p, n, n->right);
+ if (n->right)
+ c_rbnode_set_parent_and_color(n->right, p, c);
+ else
+ next = (c == C_RBNODE_BLACK) ? p : NULL;
+ } else if (!n->right) {
+ /*
+ * Case 1.1:
+ * The node has exactly one child, and it is on the left. Treat
+ * it as mirrored case of Case 1 (i.e., replace the node by its
+ * child).
+ */
+ p = c_rbnode_parent(n);
+ c = c_rbnode_color(n);
+ c_rbtree_swap_child(t, p, n, n->left);
+ c_rbnode_set_parent_and_color(n->left, p, c);
+ } else {
+ /*
+ * Case 2:
+ * We are dealing with a full interior node with a child not on
+ * both sides. Find its successor and swap it. Then remove the
+ * node similar to Case 1. For performance reasons we don't
+ * perform the full swap, but skip links that are about to be
+ * removed, anyway.
+ */
+ s = n->right;
+ if (!s->left) {
+ /* right child is next, no need to touch grandchild */
+ p = s;
+ gc = s->right;
+ } else {
+ /* find successor and swap partially */
+ s = c_rbnode_leftmost(s);
+ p = c_rbnode_parent(s);
+
+ gc = s->right;
+ p->left = s->right;
+ s->right = n->right;
+ c_rbnode_set_parent(n->right, s);
+ }
+
+ /* node is partially swapped, now remove as in Case 1 */
+ s->left = n->left;
+ c_rbnode_set_parent(n->left, s);
+
+ x = c_rbnode_parent(n);
+ c = c_rbnode_color(n);
+ c_rbtree_swap_child(t, x, n, s);
+ if (gc)
+ c_rbnode_set_parent_and_color(gc, p, C_RBNODE_BLACK);
+ else
+ next = c_rbnode_is_black(s) ? p : NULL;
+ c_rbnode_set_parent_and_color(s, x, c);
+ }
+
+ if (next)
+ c_rbtree_rebalance(t, next);
+}
diff --git a/src/basic/c-rbtree.h b/src/basic/c-rbtree.h
new file mode 100644
index 0000000000..20c5515ca1
--- /dev/null
+++ b/src/basic/c-rbtree.h
@@ -0,0 +1,297 @@
+#pragma once
+
+/***
+ This file is part of systemd. See COPYING for details.
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General 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/>.
+***/
+
+/*
+ * Standalone Red-Black-Tree Implementation in Standard ISO-C11
+ *
+ * This header provides an RB-Tree API, that is fully implemented in ISO-C11
+ * and has no external dependencies. Furthermore, tree traversal, memory
+ * allocations, and key comparisons a fully in control of the API user. The
+ * implementation only provides the RB-Tree specific rebalancing and coloring.
+ *
+ * A tree is represented by the "CRBTree" structure. It contains a *singly*
+ * field, which is a pointer to the root node. If NULL, the tree is empty. If
+ * non-NULL, there is at least a single element in the tree.
+ *
+ * Each node of the tree is represented by the "CRBNode" structure. It has
+ * three fields. The @left and @right members can be accessed by the API user
+ * directly to traverse the tree. The third member is an implementation detail
+ * and encodes the parent pointer and color of the node.
+ * API users are required to embed the CRBNode object into their own objects
+ * and then use offsetof() (i.e., container_of() and friends) to turn CRBNode
+ * pointers into pointers to their own structure.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct CRBNode CRBNode;
+typedef struct CRBTree CRBTree;
+
+/**
+ * struct CRBNode - Node of a Red-Black Tree
+ * @__parent_and_color: internal state
+ * @left: left child, or NULL
+ * @right: right child, or NULL
+ *
+ * Each node in an RB-Tree must embed an CRBNode object. This object contains
+ * pointers to its left and right child, which can be freely accessed by the
+ * API user at any time. They are NULL, if the node does not have a left/right
+ * child.
+ *
+ * The @__parent_and_color field must never be accessed directly. It encodes
+ * the pointer to the parent node, and the color of the node. Use the accessor
+ * functions instead.
+ *
+ * There is no reason to initialize a CRBNode object before linking it.
+ * However, if you need a boolean state that tells you whether the node is
+ * linked or not, you should initialize the node via c_rbnode_init() or
+ * C_RBNODE_INIT.
+ */
+struct CRBNode {
+ CRBNode *__parent_and_color;
+ CRBNode *left;
+ CRBNode *right;
+};
+
+#define C_RBNODE_INIT(_var) { .__parent_and_color = &(_var) }
+
+CRBNode *c_rbnode_leftmost(CRBNode *n);
+CRBNode *c_rbnode_rightmost(CRBNode *n);
+CRBNode *c_rbnode_next(CRBNode *n);
+CRBNode *c_rbnode_prev(CRBNode *n);
+
+/**
+ * struct CRBTree - Red-Black Tree
+ * @root: pointer to the root node, or NULL
+ *
+ * Each Red-Black Tree is rooted in an CRBTree object. This object contains a
+ * pointer to the root node of the tree. The API user is free to access the
+ * @root member at any time, and use it to traverse the tree.
+ *
+ * To initialize an RB-Tree, set it to NULL / all zero.
+ */
+struct CRBTree {
+ CRBNode *root;
+};
+
+CRBNode *c_rbtree_first(CRBTree *t);
+CRBNode *c_rbtree_last(CRBTree *t);
+
+void c_rbtree_add(CRBTree *t, CRBNode *p, CRBNode **l, CRBNode *n);
+void c_rbtree_remove(CRBTree *t, CRBNode *n);
+
+/**
+ * c_rbnode_init() - mark a node as unlinked
+ * @n: node to operate on
+ *
+ * This marks the node @n as unlinked. The node will be set to a valid state
+ * that can never happen if the node is linked in a tree. Furthermore, this
+ * state is fully known to the implementation, and as such handled gracefully
+ * in all cases.
+ *
+ * You are *NOT* required to call this on your node. c_rbtree_add() can handle
+ * uninitialized nodes just fine. However, calling this allows to use
+ * c_rbnode_is_linked() to check for the state of a node. Furthermore,
+ * iterators and accessors can be called on initialized (yet unlinked) nodes.
+ *
+ * Use the C_RBNODE_INIT macro if you want to initialize static variables.
+ */
+static inline void c_rbnode_init(CRBNode *n) {
+ *n = (CRBNode)C_RBNODE_INIT(*n);
+}
+
+/**
+ * c_rbnode_is_linked() - check whether a node is linked
+ * @n: node to check, or NULL
+ *
+ * This checks whether the passed node is linked. If you pass NULL, or if the
+ * node is not linked into a tree, this will return false. Otherwise, this
+ * returns true.
+ *
+ * Note that you must have either linked the node or initialized it, before
+ * calling this function. Never call this function on uninitialized nodes.
+ * Furthermore, removing a node via c_rbtree_remove() does *NOT* mark the node
+ * as unlinked. You have to call c_rbnode_init() yourself after removal, or use
+ * the c_rbtree_remove_init() helper.
+ *
+ * Return: true if the node is linked, false if not.
+ */
+static inline _Bool c_rbnode_is_linked(CRBNode *n) {
+ return n && n->__parent_and_color != n;
+}
+
+/**
+ * c_rbnode_parent() - return parent pointer
+ * @n node to access
+ *
+ * This returns a pointer to the parent of the given node @n. If @n does not
+ * have a parent, NULL is returned. If @n is not linked, @n itself is returned.
+ *
+ * You should not call this on unlinked or uninitialized nodes! If you do, you
+ * better know how its semantics.
+ *
+ * Return: Pointer to parent.
+ */
+static inline CRBNode *c_rbnode_parent(CRBNode *n) {
+ return (CRBNode*)((unsigned long)n->__parent_and_color & ~1UL);
+}
+
+/**
+ * c_rbtree_remove_init() - safely remove node from tree and reinitialize it
+ * @t: tree to operate on
+ * @n: node to remove, or NULL
+ *
+ * This is almost the same as c_rbtree_remove(), but extends it slightly, to be
+ * more convenient to use in many cases:
+ * - if @n is unlinked or NULL, this is a no-op
+ * - @n is reinitialized after being removed
+ */
+static inline void c_rbtree_remove_init(CRBTree *t, CRBNode *n) {
+ if (c_rbnode_is_linked(n)) {
+ c_rbtree_remove(t, n);
+ c_rbnode_init(n);
+ }
+}
+
+/**
+ * CRBCompareFunc - compare a node to a key
+ * @t: tree where the node is linked to
+ * @k: key to compare
+ * @n: node to compare
+ *
+ * If you use the tree-traversal helpers (which are optional), you need to
+ * provide this callback so they can compare nodes in a tree to the key you
+ * look for.
+ *
+ * The tree @t is provided as optional context to this callback. The key you
+ * look for is provided as @k, the current node that should be compared to is
+ * provided as @n. This function should work like strcmp(), that is, return -1
+ * if @key orders before @n, 0 if both compare equal, and 1 if it orders after
+ * @n.
+ */
+typedef int (*CRBCompareFunc) (CRBTree *t, void *k, CRBNode *n);
+
+/**
+ * c_rbtree_find_node() - find node
+ * @t: tree to search through
+ * @f: comparison function
+ * @k: key to search for
+ *
+ * This searches through @t for a node that compares equal to @k. The function
+ * @f must be provided by the caller, which is used to compare nodes to @k. See
+ * the documentation of CRBCompareFunc for details.
+ *
+ * If there are multiple entries that compare equal to @k, this will return a
+ * pseudo-randomly picked node. If you need stable lookup functions for trees
+ * where duplicate entries are allowed, you better code your own lookup.
+ *
+ * Return: Pointer to matching node, or NULL.
+ */
+static inline CRBNode *c_rbtree_find_node(CRBTree *t, CRBCompareFunc f, const void *k) {
+ CRBNode *i;
+
+ assert(t);
+ assert(f);
+
+ i = t->root;
+ while (i) {
+ int v = f(t, (void *)k, i);
+ if (v < 0)
+ i = i->left;
+ else if (v > 0)
+ i = i->right;
+ else
+ return i;
+ }
+
+ return NULL;
+}
+
+/**
+ * c_rbtree_find_entry() - find entry
+ * @_t: tree to search through
+ * @_f: comparison function
+ * @_k: key to search for
+ * @_t: type of the structure that embeds the nodes
+ * @_o: name of the node-member in type @_t
+ *
+ * This is very similar to c_rbtree_find_node(), but instead of returning a
+ * pointer to the CRBNode, it returns a pointer to the surrounding object. This
+ * object must embed the CRBNode object. The type of the surrounding object
+ * must be given as @_t, and the name of the embedded CRBNode member as @_o.
+ *
+ * See c_rbtree_find_node() for more details.
+ *
+ * Return: Pointer to found entry, NULL if not found.
+ */
+#define c_rbtree_find_entry(_m, _f, _k, _t, _o) \
+ ((_t *)(((char *)c_rbtree_find_node((_m), (_f), (_k)) ?: \
+ (char *)NULL + offsetof(_t, _o)) - offsetof(_t, _o)))
+
+/**
+ * c_rbtree_find_slot() - find slot to insert new node
+ * @t: tree to search through
+ * @f: comparison function
+ * @k: key to search for
+ * @p: output storage for parent pointer
+ *
+ * This searches through @t just like c_rbtree_find_node() does. However,
+ * instead of returning a pointer to a node that compares equal to @k, this
+ * searches for a slot to insert a node with key @k. A pointer to the slot is
+ * returned, and a pointer to the parent of the slot is stored in @p. Both
+ * can be passed directly to c_rbtree_add(), together with your node to insert.
+ *
+ * If there already is a node in the tree, that compares equal to @k, this will
+ * return NULL and store the conflicting node in @p. In all other cases,
+ * this will return a pointer (non-NULL) to the empty slot to insert the node
+ * at. @p will point to the parent node of that slot.
+ *
+ * If you want trees that allow duplicate nodes, you better code your own
+ * insertion function.
+ *
+ * Return: Pointer to slot to insert node, or NULL on conflicts.
+ */
+static inline CRBNode **c_rbtree_find_slot(CRBTree *t, CRBCompareFunc f, const void *k, CRBNode **p) {
+ CRBNode **i;
+
+ assert(t);
+ assert(f);
+ assert(p);
+
+ i = &t->root;
+ *p = NULL;
+ while (*i) {
+ int v = f(t, (void *)k, *i);
+ *p = *i;
+ if (v < 0)
+ i = &(*i)->left;
+ else if (v > 0)
+ i = &(*i)->right;
+ else
+ return NULL;
+ }
+
+ return i;
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/basic/calendarspec.c b/src/basic/calendarspec.c
index 2dcc9c5575..8f83d9c142 100644
--- a/src/basic/calendarspec.c
+++ b/src/basic/calendarspec.c
@@ -19,10 +19,20 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <alloca.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <time.h>
+#include "alloc-util.h"
#include "calendarspec.h"
+#include "fileio.h"
+#include "macro.h"
+#include "parse-util.h"
+#include "string-util.h"
#define BITS_WEEKDAYS 127
@@ -46,7 +56,7 @@ void calendar_spec_free(CalendarSpec *c) {
free_chain(c->day);
free_chain(c->hour);
free_chain(c->minute);
- free_chain(c->second);
+ free_chain(c->microsecond);
free(c);
}
@@ -132,7 +142,7 @@ int calendar_spec_normalize(CalendarSpec *c) {
sort_chain(&c->day);
sort_chain(&c->hour);
sort_chain(&c->minute);
- sort_chain(&c->second);
+ sort_chain(&c->microsecond);
return 0;
}
@@ -174,7 +184,7 @@ _pure_ bool calendar_spec_valid(CalendarSpec *c) {
if (!chain_valid(c->minute, 0, 59))
return false;
- if (!chain_valid(c->second, 0, 59))
+ if (!chain_valid(c->microsecond, 0, 60*USEC_PER_SEC-1))
return false;
return true;
@@ -229,7 +239,7 @@ static void format_weekdays(FILE *f, const CalendarSpec *c) {
}
}
-static void format_chain(FILE *f, int space, const CalendarComponent *c) {
+static void format_chain(FILE *f, int space, const CalendarComponent *c, bool usec) {
assert(f);
if (!c) {
@@ -238,14 +248,25 @@ static void format_chain(FILE *f, int space, const CalendarComponent *c) {
}
assert(c->value >= 0);
- fprintf(f, "%0*i", space, c->value);
-
- if (c->repeat > 0)
- fprintf(f, "/%i", c->repeat);
+ if (!usec)
+ fprintf(f, "%0*i", space, c->value);
+ else if (c->value % USEC_PER_SEC == 0)
+ fprintf(f, "%0*i", space, (int) (c->value / USEC_PER_SEC));
+ else
+ fprintf(f, "%0*i.%06i", space, (int) (c->value / USEC_PER_SEC), (int) (c->value % USEC_PER_SEC));
+
+ if (c->repeat > 0) {
+ if (!usec)
+ fprintf(f, "/%i", c->repeat);
+ else if (c->repeat % USEC_PER_SEC == 0)
+ fprintf(f, "/%i", (int) (c->repeat / USEC_PER_SEC));
+ else
+ fprintf(f, "/%i.%06i", (int) (c->repeat / USEC_PER_SEC), (int) (c->repeat % USEC_PER_SEC));
+ }
if (c->next) {
fputc(',', f);
- format_chain(f, space, c->next);
+ format_chain(f, space, c->next, usec);
}
}
@@ -267,17 +288,20 @@ int calendar_spec_to_string(const CalendarSpec *c, char **p) {
fputc(' ', f);
}
- format_chain(f, 4, c->year);
+ format_chain(f, 4, c->year, false);
fputc('-', f);
- format_chain(f, 2, c->month);
+ format_chain(f, 2, c->month, false);
fputc('-', f);
- format_chain(f, 2, c->day);
+ format_chain(f, 2, c->day, false);
fputc(' ', f);
- format_chain(f, 2, c->hour);
+ format_chain(f, 2, c->hour, false);
fputc(':', f);
- format_chain(f, 2, c->minute);
+ format_chain(f, 2, c->minute, false);
fputc(':', f);
- format_chain(f, 2, c->second);
+ format_chain(f, 2, c->microsecond, true);
+
+ if (c->utc)
+ fputs(" UTC", f);
r = fflush_and_check(f);
if (r < 0) {
@@ -385,35 +409,70 @@ static int parse_weekdays(const char **p, CalendarSpec *c) {
}
}
-static int prepend_component(const char **p, CalendarComponent **c) {
- unsigned long value, repeat = 0;
- char *e = NULL, *ee = NULL;
- CalendarComponent *cc;
-
- assert(p);
- assert(c);
+static int parse_component_decimal(const char **p, bool usec, unsigned long *res) {
+ unsigned long value;
+ const char *e = NULL;
+ char *ee = NULL;
+ int r;
errno = 0;
- value = strtoul(*p, &e, 10);
+ value = strtoul(*p, &ee, 10);
if (errno > 0)
return -errno;
- if (e == *p)
+ if (ee == *p)
return -EINVAL;
if ((unsigned long) (int) value != value)
return -ERANGE;
+ e = ee;
- if (*e == '/') {
- repeat = strtoul(e+1, &ee, 10);
- if (errno > 0)
- return -errno;
- if (ee == e+1)
- return -EINVAL;
- if ((unsigned long) (int) repeat != repeat)
- return -ERANGE;
- if (repeat <= 0)
+ if (usec) {
+ if (value * USEC_PER_SEC / USEC_PER_SEC != value)
return -ERANGE;
- e = ee;
+ value *= USEC_PER_SEC;
+ if (*e == '.') {
+ unsigned add;
+
+ e++;
+ r = parse_fractional_part_u(&e, 6, &add);
+ if (r < 0)
+ return r;
+
+ if (add + value < value)
+ return -ERANGE;
+ value += add;
+ }
+ }
+
+ *p = e;
+ *res = value;
+
+ return 0;
+}
+
+static int prepend_component(const char **p, bool usec, CalendarComponent **c) {
+ unsigned long value, repeat = 0;
+ CalendarComponent *cc;
+ int r;
+ const char *e;
+
+ assert(p);
+ assert(c);
+
+ e = *p;
+
+ r = parse_component_decimal(&e, usec, &value);
+ if (r < 0)
+ return r;
+
+ if (*e == '/') {
+ e++;
+ r = parse_component_decimal(&e, usec, &repeat);
+ if (r < 0)
+ return r;
+
+ if (repeat == 0)
+ return -ERANGE;
}
if (*e != 0 && *e != ' ' && *e != ',' && *e != '-' && *e != ':')
@@ -432,13 +491,31 @@ static int prepend_component(const char **p, CalendarComponent **c) {
if (*e ==',') {
*p += 1;
- return prepend_component(p, c);
+ return prepend_component(p, usec, c);
}
return 0;
}
-static int parse_chain(const char **p, CalendarComponent **c) {
+static int const_chain(int value, CalendarComponent **c) {
+ CalendarComponent *cc = NULL;
+
+ assert(c);
+
+ cc = new0(CalendarComponent, 1);
+ if (!cc)
+ return -ENOMEM;
+
+ cc->value = value;
+ cc->repeat = 0;
+ cc->next = *c;
+
+ *c = cc;
+
+ return 0;
+}
+
+static int parse_chain(const char **p, bool usec, CalendarComponent **c) {
const char *t;
CalendarComponent *cc = NULL;
int r;
@@ -449,12 +526,19 @@ static int parse_chain(const char **p, CalendarComponent **c) {
t = *p;
if (t[0] == '*') {
+ if (usec) {
+ r = const_chain(0, c);
+ if (r < 0)
+ return r;
+ (*c)->repeat = USEC_PER_SEC;
+ } else
+ *c = NULL;
+
*p = t + 1;
- *c = NULL;
return 0;
}
- r = prepend_component(&t, &cc);
+ r = prepend_component(&t, usec, &cc);
if (r < 0) {
free_chain(cc);
return r;
@@ -465,24 +549,6 @@ static int parse_chain(const char **p, CalendarComponent **c) {
return 0;
}
-static int const_chain(int value, CalendarComponent **c) {
- CalendarComponent *cc = NULL;
-
- assert(c);
-
- cc = new0(CalendarComponent, 1);
- if (!cc)
- return -ENOMEM;
-
- cc->value = value;
- cc->repeat = 0;
- cc->next = *c;
-
- *c = cc;
-
- return 0;
-}
-
static int parse_date(const char **p, CalendarSpec *c) {
const char *t;
int r;
@@ -497,7 +563,7 @@ static int parse_date(const char **p, CalendarSpec *c) {
if (*t == 0)
return 0;
- r = parse_chain(&t, &first);
+ r = parse_chain(&t, false, &first);
if (r < 0)
return r;
@@ -513,7 +579,7 @@ static int parse_date(const char **p, CalendarSpec *c) {
}
t++;
- r = parse_chain(&t, &second);
+ r = parse_chain(&t, false, &second);
if (r < 0) {
free_chain(first);
return r;
@@ -534,7 +600,7 @@ static int parse_date(const char **p, CalendarSpec *c) {
}
t++;
- r = parse_chain(&t, &third);
+ r = parse_chain(&t, false, &third);
if (r < 0) {
free_chain(first);
free_chain(second);
@@ -556,7 +622,7 @@ static int parse_date(const char **p, CalendarSpec *c) {
return -EINVAL;
}
-static int parse_time(const char **p, CalendarSpec *c) {
+static int parse_calendar_time(const char **p, CalendarSpec *c) {
CalendarComponent *h = NULL, *m = NULL, *s = NULL;
const char *t;
int r;
@@ -576,7 +642,7 @@ static int parse_time(const char **p, CalendarSpec *c) {
goto finish;
}
- r = parse_chain(&t, &h);
+ r = parse_chain(&t, false, &h);
if (r < 0)
goto fail;
@@ -586,7 +652,7 @@ static int parse_time(const char **p, CalendarSpec *c) {
}
t++;
- r = parse_chain(&t, &m);
+ r = parse_chain(&t, false, &m);
if (r < 0)
goto fail;
@@ -604,7 +670,7 @@ static int parse_time(const char **p, CalendarSpec *c) {
}
t++;
- r = parse_chain(&t, &s);
+ r = parse_chain(&t, true, &s);
if (r < 0)
goto fail;
@@ -633,7 +699,8 @@ finish:
*p = t;
c->hour = h;
c->minute = m;
- c->second = s;
+ c->microsecond = s;
+
return 0;
fail:
@@ -646,6 +713,7 @@ fail:
int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
CalendarSpec *c;
int r;
+ const char *utc;
assert(p);
assert(spec);
@@ -657,8 +725,14 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
if (!c)
return -ENOMEM;
+ utc = endswith_no_case(p, " UTC");
+ if (utc) {
+ c->utc = true;
+ p = strndupa(p, utc - p);
+ }
+
if (strcaseeq(p, "minutely")) {
- r = const_chain(0, &c->second);
+ r = const_chain(0, &c->microsecond);
if (r < 0)
goto fail;
@@ -666,7 +740,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
r = const_chain(0, &c->minute);
if (r < 0)
goto fail;
- r = const_chain(0, &c->second);
+ r = const_chain(0, &c->microsecond);
if (r < 0)
goto fail;
@@ -677,7 +751,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
r = const_chain(0, &c->minute);
if (r < 0)
goto fail;
- r = const_chain(0, &c->second);
+ r = const_chain(0, &c->microsecond);
if (r < 0)
goto fail;
@@ -691,7 +765,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
r = const_chain(0, &c->minute);
if (r < 0)
goto fail;
- r = const_chain(0, &c->second);
+ r = const_chain(0, &c->microsecond);
if (r < 0)
goto fail;
@@ -711,7 +785,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
r = const_chain(0, &c->minute);
if (r < 0)
goto fail;
- r = const_chain(0, &c->second);
+ r = const_chain(0, &c->microsecond);
if (r < 0)
goto fail;
@@ -725,7 +799,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
r = const_chain(0, &c->minute);
if (r < 0)
goto fail;
- r = const_chain(0, &c->second);
+ r = const_chain(0, &c->microsecond);
if (r < 0)
goto fail;
@@ -752,7 +826,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
r = const_chain(0, &c->minute);
if (r < 0)
goto fail;
- r = const_chain(0, &c->second);
+ r = const_chain(0, &c->microsecond);
if (r < 0)
goto fail;
@@ -776,7 +850,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
r = const_chain(0, &c->minute);
if (r < 0)
goto fail;
- r = const_chain(0, &c->second);
+ r = const_chain(0, &c->microsecond);
if (r < 0)
goto fail;
@@ -789,7 +863,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
if (r < 0)
goto fail;
- r = parse_time(&p, c);
+ r = parse_calendar_time(&p, c);
if (r < 0)
goto fail;
@@ -859,13 +933,13 @@ static int find_matching_component(const CalendarComponent *c, int *val) {
return r;
}
-static bool tm_out_of_bounds(const struct tm *tm) {
+static bool tm_out_of_bounds(const struct tm *tm, bool utc) {
struct tm t;
assert(tm);
t = *tm;
- if (mktime(&t) == (time_t) -1)
+ if (mktime_or_timegm(&t, utc) == (time_t) -1)
return true;
/* Did any normalization take place? If so, it was out of bounds before */
@@ -878,7 +952,7 @@ static bool tm_out_of_bounds(const struct tm *tm) {
t.tm_sec != tm->tm_sec;
}
-static bool matches_weekday(int weekdays_bits, const struct tm *tm) {
+static bool matches_weekday(int weekdays_bits, const struct tm *tm, bool utc) {
struct tm t;
int k;
@@ -886,25 +960,27 @@ static bool matches_weekday(int weekdays_bits, const struct tm *tm) {
return true;
t = *tm;
- if (mktime(&t) == (time_t) -1)
+ if (mktime_or_timegm(&t, utc) == (time_t) -1)
return false;
k = t.tm_wday == 0 ? 6 : t.tm_wday - 1;
return (weekdays_bits & (1 << k));
}
-static int find_next(const CalendarSpec *spec, struct tm *tm) {
+static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) {
struct tm c;
+ int tm_usec;
int r;
assert(spec);
assert(tm);
c = *tm;
+ tm_usec = *usec;
for (;;) {
/* Normalize the current date */
- mktime(&c);
+ mktime_or_timegm(&c, spec->utc);
c.tm_isdst = -1;
c.tm_year += 1900;
@@ -914,9 +990,9 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) {
if (r > 0) {
c.tm_mon = 0;
c.tm_mday = 1;
- c.tm_hour = c.tm_min = c.tm_sec = 0;
+ c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
}
- if (r < 0 || tm_out_of_bounds(&c))
+ if (r < 0 || tm_out_of_bounds(&c, spec->utc))
return r;
c.tm_mon += 1;
@@ -925,59 +1001,63 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) {
if (r > 0) {
c.tm_mday = 1;
- c.tm_hour = c.tm_min = c.tm_sec = 0;
+ c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
}
- if (r < 0 || tm_out_of_bounds(&c)) {
+ if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
c.tm_year ++;
c.tm_mon = 0;
c.tm_mday = 1;
- c.tm_hour = c.tm_min = c.tm_sec = 0;
+ c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
continue;
}
r = find_matching_component(spec->day, &c.tm_mday);
if (r > 0)
- c.tm_hour = c.tm_min = c.tm_sec = 0;
- if (r < 0 || tm_out_of_bounds(&c)) {
+ c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
+ if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
c.tm_mon ++;
c.tm_mday = 1;
- c.tm_hour = c.tm_min = c.tm_sec = 0;
+ c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
continue;
}
- if (!matches_weekday(spec->weekdays_bits, &c)) {
+ if (!matches_weekday(spec->weekdays_bits, &c, spec->utc)) {
c.tm_mday++;
- c.tm_hour = c.tm_min = c.tm_sec = 0;
+ c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
continue;
}
r = find_matching_component(spec->hour, &c.tm_hour);
if (r > 0)
c.tm_min = c.tm_sec = 0;
- if (r < 0 || tm_out_of_bounds(&c)) {
+ if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
c.tm_mday ++;
- c.tm_hour = c.tm_min = c.tm_sec = 0;
+ c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
continue;
}
r = find_matching_component(spec->minute, &c.tm_min);
if (r > 0)
c.tm_sec = 0;
- if (r < 0 || tm_out_of_bounds(&c)) {
+ if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
c.tm_hour ++;
- c.tm_min = c.tm_sec = 0;
+ c.tm_min = c.tm_sec = tm_usec = 0;
continue;
}
- r = find_matching_component(spec->second, &c.tm_sec);
- if (r < 0 || tm_out_of_bounds(&c)) {
+ c.tm_sec = c.tm_sec * USEC_PER_SEC + tm_usec;
+ r = find_matching_component(spec->microsecond, &c.tm_sec);
+ tm_usec = c.tm_sec % USEC_PER_SEC;
+ c.tm_sec /= USEC_PER_SEC;
+
+ if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
c.tm_min ++;
- c.tm_sec = 0;
+ c.tm_sec = tm_usec = 0;
continue;
}
-
*tm = c;
+ *usec = tm_usec;
return 0;
}
}
@@ -986,21 +1066,24 @@ int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next)
struct tm tm;
time_t t;
int r;
+ usec_t tm_usec;
assert(spec);
assert(next);
- t = (time_t) (usec / USEC_PER_SEC) + 1;
- assert_se(localtime_r(&t, &tm));
+ usec++;
+ t = (time_t) (usec / USEC_PER_SEC);
+ assert_se(localtime_or_gmtime_r(&t, &tm, spec->utc));
+ tm_usec = usec % USEC_PER_SEC;
- r = find_next(spec, &tm);
+ r = find_next(spec, &tm, &tm_usec);
if (r < 0)
return r;
- t = mktime(&tm);
+ t = mktime_or_timegm(&tm, spec->utc);
if (t == (time_t) -1)
return -EINVAL;
- *next = (usec_t) t * USEC_PER_SEC;
+ *next = (usec_t) t * USEC_PER_SEC + tm_usec;
return 0;
}
diff --git a/src/basic/calendarspec.h b/src/basic/calendarspec.h
index 7baf318249..4e3aa9e1d8 100644
--- a/src/basic/calendarspec.h
+++ b/src/basic/calendarspec.h
@@ -25,6 +25,8 @@
* time, a la cron */
#include <stdbool.h>
+
+#include "time-util.h"
#include "util.h"
typedef struct CalendarComponent {
@@ -36,6 +38,7 @@ typedef struct CalendarComponent {
typedef struct CalendarSpec {
int weekdays_bits;
+ bool utc;
CalendarComponent *year;
CalendarComponent *month;
@@ -43,7 +46,7 @@ typedef struct CalendarSpec {
CalendarComponent *hour;
CalendarComponent *minute;
- CalendarComponent *second;
+ CalendarComponent *microsecond;
} CalendarSpec;
void calendar_spec_free(CalendarSpec *c);
diff --git a/src/basic/cap-list.c b/src/basic/cap-list.c
index bd5bffbfa5..0e5cc452b9 100644
--- a/src/basic/cap-list.c
+++ b/src/basic/cap-list.c
@@ -19,16 +19,19 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
#include <string.h>
-#include "util.h"
#include "cap-list.h"
+#include "macro.h"
#include "missing.h"
+#include "parse-util.h"
+#include "util.h"
static const struct capability_name* lookup_capability(register const char *str, register unsigned int len);
-#include "cap-to-name.h"
#include "cap-from-name.h"
+#include "cap-to-name.h"
const char *capability_to_name(int id) {
diff --git a/src/basic/capability.c b/src/basic/capability-util.c
index 8dbe4da5bb..49c2d61afe 100644
--- a/src/basic/capability.c
+++ b/src/basic/capability-util.c
@@ -19,18 +19,21 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
#include <errno.h>
+#include <grp.h>
#include <stdio.h>
+#include <stdlib.h>
#include <sys/capability.h>
#include <sys/prctl.h>
-#include "grp.h"
+#include <unistd.h>
+#include "alloc-util.h"
+#include "capability-util.h"
+#include "fileio.h"
+#include "log.h"
#include "macro.h"
+#include "parse-util.h"
#include "util.h"
-#include "log.h"
-#include "fileio.h"
-#include "capability.h"
int have_effective_cap(int value) {
_cleanup_cap_free_ cap_t cap;
@@ -93,7 +96,62 @@ unsigned long cap_last_cap(void) {
return p;
}
-int capability_bounding_set_drop(uint64_t drop, bool right_now) {
+int capability_update_inherited_set(cap_t caps, uint64_t set) {
+ unsigned long i;
+
+ /* Add capabilities in the set to the inherited caps. Do not apply
+ * them yet. */
+
+ for (i = 0; i < cap_last_cap(); i++) {
+
+ if (set & (UINT64_C(1) << i)) {
+ cap_value_t v;
+
+ v = (cap_value_t) i;
+
+ /* Make the capability inheritable. */
+ if (cap_set_flag(caps, CAP_INHERITABLE, 1, &v, CAP_SET) < 0)
+ return -errno;
+ }
+ }
+
+ return 0;
+}
+
+int capability_ambient_set_apply(uint64_t set, bool also_inherit) {
+ unsigned long i;
+ _cleanup_cap_free_ cap_t caps = NULL;
+
+ /* Add the capabilities to the ambient set. */
+
+ if (also_inherit) {
+ int r;
+ caps = cap_get_proc();
+ if (!caps)
+ return -errno;
+
+ r = capability_update_inherited_set(caps, set);
+ if (r < 0)
+ return -errno;
+
+ if (cap_set_proc(caps) < 0)
+ return -errno;
+ }
+
+ for (i = 0; i < cap_last_cap(); i++) {
+
+ if (set & (UINT64_C(1) << i)) {
+
+ /* Add the capability to the ambient set. */
+ if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0, 0) < 0)
+ return -errno;
+ }
+ }
+
+ return 0;
+}
+
+int capability_bounding_set_drop(uint64_t keep, bool right_now) {
_cleanup_cap_free_ cap_t after_cap = NULL;
cap_flag_value_t fv;
unsigned long i;
@@ -134,7 +192,7 @@ int capability_bounding_set_drop(uint64_t drop, bool right_now) {
for (i = 0; i <= cap_last_cap(); i++) {
- if (drop & ((uint64_t) 1ULL << (uint64_t) i)) {
+ if (!(keep & (UINT64_C(1) << i))) {
cap_value_t v;
/* Drop it from the bounding set */
@@ -173,7 +231,7 @@ finish:
return r;
}
-static int drop_from_file(const char *fn, uint64_t drop) {
+static int drop_from_file(const char *fn, uint64_t keep) {
int r, k;
uint32_t hi, lo;
uint64_t current, after;
@@ -193,7 +251,7 @@ static int drop_from_file(const char *fn, uint64_t drop) {
return -EIO;
current = (uint64_t) lo | ((uint64_t) hi << 32ULL);
- after = current & ~drop;
+ after = current & keep;
if (current == after)
return 0;
@@ -210,14 +268,14 @@ static int drop_from_file(const char *fn, uint64_t drop) {
return r;
}
-int capability_bounding_set_drop_usermode(uint64_t drop) {
+int capability_bounding_set_drop_usermode(uint64_t keep) {
int r;
- r = drop_from_file("/proc/sys/kernel/usermodehelper/inheritable", drop);
+ r = drop_from_file("/proc/sys/kernel/usermodehelper/inheritable", keep);
if (r < 0)
return r;
- r = drop_from_file("/proc/sys/kernel/usermodehelper/bset", drop);
+ r = drop_from_file("/proc/sys/kernel/usermodehelper/bset", keep);
if (r < 0)
return r;
@@ -254,7 +312,7 @@ int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) {
return log_error_errno(errno, "Failed to disable keep capabilities flag: %m");
/* Drop all caps from the bounding set, except the ones we want */
- r = capability_bounding_set_drop(~keep_capabilities, true);
+ r = capability_bounding_set_drop(keep_capabilities, true);
if (r < 0)
return log_error_errno(r, "Failed to drop capabilities: %m");
@@ -276,10 +334,8 @@ int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) {
assert(keep_capabilities & (1ULL << (i - 1)));
if (cap_set_flag(d, CAP_EFFECTIVE, j, bits, CAP_SET) < 0 ||
- cap_set_flag(d, CAP_PERMITTED, j, bits, CAP_SET) < 0) {
- log_error_errno(errno, "Failed to enable capabilities bits: %m");
- return -errno;
- }
+ cap_set_flag(d, CAP_PERMITTED, j, bits, CAP_SET) < 0)
+ return log_error_errno(errno, "Failed to enable capabilities bits: %m");
if (cap_set_proc(d) < 0)
return log_error_errno(errno, "Failed to increase capabilities: %m");
diff --git a/src/basic/capability.h b/src/basic/capability-util.h
index 4eb5c2a835..be41475441 100644
--- a/src/basic/capability.h
+++ b/src/basic/capability-util.h
@@ -22,14 +22,22 @@
***/
#include <stdbool.h>
+#include <stdint.h>
#include <sys/capability.h>
+#include <sys/types.h>
+#include "macro.h"
#include "util.h"
+#define CAP_ALL (uint64_t) -1
+
unsigned long cap_last_cap(void);
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 capability_bounding_set_drop(uint64_t keep, bool right_now);
+int capability_bounding_set_drop_usermode(uint64_t keep);
+
+int capability_ambient_set_apply(uint64_t set, bool also_inherit);
+int capability_update_inherited_set(cap_t caps, uint64_t ambient_set);
int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities);
@@ -43,3 +51,9 @@ static inline void cap_free_charpp(char **p) {
cap_free(*p);
}
#define _cleanup_cap_free_charp_ _cleanup_(cap_free_charpp)
+
+static inline bool cap_test_all(uint64_t caps) {
+ uint64_t m;
+ m = (UINT64_C(1) << (cap_last_cap() + 1)) - 1;
+ return (caps & m) == m;
+}
diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c
index 95fc2b9e5d..f873fb89d3 100644
--- a/src/basic/cgroup-util.c
+++ b/src/basic/cgroup-util.c
@@ -19,28 +19,45 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <dirent.h>
#include <errno.h>
-#include <unistd.h>
+#include <ftw.h>
+#include <limits.h>
#include <signal.h>
-#include <string.h>
+#include <stddef.h>
#include <stdlib.h>
-#include <dirent.h>
+#include <string.h>
#include <sys/stat.h>
+#include <sys/statfs.h>
#include <sys/types.h>
-#include <ftw.h>
+#include <unistd.h>
-#include "set.h"
-#include "macro.h"
-#include "util.h"
+#include "alloc-util.h"
+#include "cgroup-util.h"
+#include "def.h"
+#include "dirent-util.h"
+#include "extract-word.h"
+#include "fd-util.h"
+#include "fileio.h"
#include "formats-util.h"
-#include "process-util.h"
+#include "fs-util.h"
+#include "log.h"
+#include "login-util.h"
+#include "macro.h"
+#include "missing.h"
+#include "mkdir.h"
+#include "parse-util.h"
#include "path-util.h"
-#include "unit-name.h"
-#include "fileio.h"
+#include "proc-cmdline.h"
+#include "process-util.h"
+#include "set.h"
#include "special.h"
-#include "mkdir.h"
-#include "login-util.h"
-#include "cgroup-util.h"
+#include "stat-util.h"
+#include "stdio-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "unit-name.h"
+#include "user-util.h"
int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) {
_cleanup_free_ char *fs = NULL;
@@ -76,7 +93,7 @@ int cg_read_pid(FILE *f, pid_t *_pid) {
if (feof(f))
return 0;
- return errno ? -errno : -EIO;
+ return errno > 0 ? -errno : -EIO;
}
if (ul <= 0)
@@ -631,7 +648,7 @@ int cg_trim(const char *controller, const char *path, bool delete_root) {
if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) != 0) {
if (errno == ENOENT)
r = 0;
- else if (errno != 0)
+ else if (errno > 0)
r = -errno;
else
r = -EIO;
@@ -700,7 +717,7 @@ int cg_attach(const char *controller, const char *path, pid_t pid) {
if (pid == 0)
pid = getpid();
- snprintf(c, sizeof(c), PID_FMT"\n", pid);
+ xsprintf(c, PID_FMT "\n", pid);
return write_string_file(fs, c, 0);
}
@@ -2074,7 +2091,7 @@ int cg_kernel_controllers(Set *controllers) {
if (feof(f))
break;
- if (ferror(f) && errno != 0)
+ if (ferror(f) && errno > 0)
return -errno;
return -EBADMSG;
@@ -2119,7 +2136,7 @@ int cg_unified(void) {
else if (F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC))
unified_cache = false;
else
- return -ENOEXEC;
+ return -ENOMEDIUM;
return unified_cache;
}
diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h
index 01359fa7cb..661785784a 100644
--- a/src/basic/cgroup-util.h
+++ b/src/basic/cgroup-util.h
@@ -21,12 +21,16 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
-#include <stdio.h>
#include <dirent.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/types.h>
-#include "set.h"
#include "def.h"
+#include "hashmap.h"
+#include "macro.h"
+#include "set.h"
/* An enum of well known cgroup controllers */
typedef enum CGroupController {
diff --git a/src/basic/chattr-util.c b/src/basic/chattr-util.c
new file mode 100644
index 0000000000..438fc63c5c
--- /dev/null
+++ b/src/basic/chattr-util.c
@@ -0,0 +1,109 @@
+/*-*- 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 <errno.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <linux/fs.h>
+
+#include "chattr-util.h"
+#include "fd-util.h"
+#include "macro.h"
+
+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;
+
+ 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 1;
+}
+
+int chattr_path(const char *p, unsigned value, unsigned mask) {
+ _cleanup_close_ int fd = -1;
+
+ assert(p);
+
+ if (mask == 0)
+ return 0;
+
+ fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
+ if (fd < 0)
+ return -errno;
+
+ 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;
+
+ return 0;
+}
+
+int read_attr_path(const char *p, unsigned *ret) {
+ _cleanup_close_ int fd = -1;
+
+ assert(p);
+ assert(ret);
+
+ fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
+ if (fd < 0)
+ return -errno;
+
+ return read_attr_fd(fd, ret);
+}
diff --git a/src/basic/chattr-util.h b/src/basic/chattr-util.h
new file mode 100644
index 0000000000..ba6b8eb5c1
--- /dev/null
+++ b/src/basic/chattr-util.h
@@ -0,0 +1,28 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+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);
diff --git a/src/basic/clock-util.c b/src/basic/clock-util.c
index e4e03df1e4..05788a360e 100644
--- a/src/basic/clock-util.c
+++ b/src/basic/clock-util.c
@@ -20,15 +20,20 @@
***/
#include <errno.h>
-#include <stdio.h>
#include <fcntl.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <time.h>
+#include <linux/rtc.h>
+#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/time.h>
-#include <linux/rtc.h>
+#include "clock-util.h"
+#include "fd-util.h"
#include "macro.h"
+#include "string-util.h"
#include "util.h"
-#include "clock-util.h"
int clock_get_hwclock(struct tm *tm) {
_cleanup_close_ int fd = -1;
@@ -117,7 +122,8 @@ int clock_set_timezone(int *min) {
* have read from the RTC.
*/
if (settimeofday(tv_null, &tz) < 0)
- return -errno;
+ return negative_errno();
+
if (min)
*min = minutesdelta;
return 0;
diff --git a/src/basic/clock-util.h b/src/basic/clock-util.h
index 8c2d235430..fef2d471a6 100644
--- a/src/basic/clock-util.h
+++ b/src/basic/clock-util.h
@@ -21,6 +21,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <time.h>
int clock_is_localtime(void);
int clock_set_timezone(int *min);
diff --git a/src/basic/conf-files.c b/src/basic/conf-files.c
index da8745b284..5854caeb51 100644
--- a/src/basic/conf-files.c
+++ b/src/basic/conf-files.c
@@ -19,24 +19,29 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <string.h>
+#include <dirent.h>
#include <errno.h>
-#include <stdlib.h>
+#include <stdarg.h>
#include <stdio.h>
-#include <dirent.h>
+#include <stdlib.h>
+#include <string.h>
+#include "conf-files.h"
+#include "dirent-util.h"
+#include "fd-util.h"
+#include "hashmap.h"
+#include "log.h"
#include "macro.h"
-#include "util.h"
#include "missing.h"
-#include "log.h"
-#include "strv.h"
#include "path-util.h"
-#include "hashmap.h"
-#include "conf-files.h"
+#include "string-util.h"
+#include "strv.h"
+#include "util.h"
static int files_add(Hashmap *h, const char *root, const char *path, const char *suffix) {
_cleanup_closedir_ DIR *dir = NULL;
const char *dirpath;
+ struct dirent *de;
int r;
assert(path);
@@ -51,18 +56,9 @@ static int files_add(Hashmap *h, const char *root, const char *path, const char
return -errno;
}
- for (;;) {
- struct dirent *de;
+ FOREACH_DIRENT(de, dir, return -errno) {
char *p;
- errno = 0;
- de = readdir(dir);
- if (!de && errno != 0)
- return -errno;
-
- if (!de)
- break;
-
if (!dirent_is_file_with_suffix(de, suffix))
continue;
@@ -112,17 +108,15 @@ static int conf_files_list_strv_internal(char ***strv, const char *suffix, const
STRV_FOREACH(p, dirs) {
r = files_add(fh, root, *p, suffix);
- if (r == -ENOMEM) {
+ if (r == -ENOMEM)
return r;
- } else if (r < 0)
- log_debug_errno(r, "Failed to search for files in %s: %m",
- *p);
+ if (r < 0)
+ log_debug_errno(r, "Failed to search for files in %s, ignoring: %m", *p);
}
files = hashmap_get_strv(fh);
- if (files == NULL) {
+ if (!files)
return -ENOMEM;
- }
qsort_safe(files, hashmap_size(fh), sizeof(char *), base_cmp);
*strv = files;
diff --git a/src/basic/copy.c b/src/basic/copy.c
index b20c178727..024712d290 100644
--- a/src/basic/copy.c
+++ b/src/basic/copy.c
@@ -19,13 +19,34 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <sys/sendfile.h>
+#include <sys/stat.h>
#include <sys/xattr.h>
+#include <time.h>
+#include <unistd.h>
-#include "util.h"
+#include "alloc-util.h"
#include "btrfs-util.h"
-#include "strv.h"
+#include "chattr-util.h"
#include "copy.h"
+#include "dirent-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "io-util.h"
+#include "macro.h"
+#include "string-util.h"
+#include "strv.h"
+#include "time-util.h"
+#include "umask-util.h"
+#include "xattr-util.h"
#define COPY_BUFFER_SIZE (16*1024)
diff --git a/src/basic/copy.h b/src/basic/copy.h
index ba0890b442..b3fc2bb709 100644
--- a/src/basic/copy.h
+++ b/src/basic/copy.h
@@ -23,6 +23,7 @@
#include <inttypes.h>
#include <stdbool.h>
+#include <stdint.h>
#include <sys/types.h>
int copy_file_fd(const char *from, int to, bool try_reflink);
diff --git a/src/basic/cpu-set-util.c b/src/basic/cpu-set-util.c
new file mode 100644
index 0000000000..85b7519953
--- /dev/null
+++ b/src/basic/cpu-set-util.c
@@ -0,0 +1,116 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010-2015 Lennart Poettering
+ Copyright 2015 Filipe Brandenburger
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General 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 <stddef.h>
+#include <syslog.h>
+
+#include "alloc-util.h"
+#include "cpu-set-util.h"
+#include "extract-word.h"
+#include "log.h"
+#include "macro.h"
+#include "parse-util.h"
+#include "string-util.h"
+
+cpu_set_t* cpu_set_malloc(unsigned *ncpus) {
+ cpu_set_t *c;
+ unsigned n = 1024;
+
+ /* Allocates the cpuset in the right size */
+
+ for (;;) {
+ c = CPU_ALLOC(n);
+ if (!c)
+ return NULL;
+
+ if (sched_getaffinity(0, CPU_ALLOC_SIZE(n), c) >= 0) {
+ CPU_ZERO_S(CPU_ALLOC_SIZE(n), c);
+
+ if (ncpus)
+ *ncpus = n;
+
+ return c;
+ }
+
+ CPU_FREE(c);
+
+ if (errno != EINVAL)
+ return NULL;
+
+ n *= 2;
+ }
+}
+
+int parse_cpu_set_and_warn(
+ const char *rvalue,
+ cpu_set_t **cpu_set,
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *lvalue) {
+
+ const char *whole_rvalue = rvalue;
+ _cleanup_cpu_free_ cpu_set_t *c = NULL;
+ unsigned ncpus = 0;
+
+ assert(lvalue);
+ assert(rvalue);
+
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
+ unsigned cpu, cpu_lower, cpu_upper;
+ int r;
+
+ r = extract_first_word(&rvalue, &word, WHITESPACE ",", EXTRACT_QUOTES);
+ if (r < 0)
+ return log_syntax(unit, LOG_ERR, filename, line, r, "Invalid value for %s: %s", lvalue, whole_rvalue);
+ if (r == 0)
+ break;
+
+ if (!c) {
+ c = cpu_set_malloc(&ncpus);
+ if (!c)
+ return log_oom();
+ }
+
+ r = parse_range(word, &cpu_lower, &cpu_upper);
+ if (r < 0)
+ return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse CPU affinity '%s'", word);
+ if (cpu_lower >= ncpus || cpu_upper >= ncpus)
+ return log_syntax(unit, LOG_ERR, filename, line, EINVAL, "CPU out of range '%s' ncpus is %u", word, ncpus);
+
+ if (cpu_lower > cpu_upper)
+ log_syntax(unit, LOG_WARNING, filename, line, 0, "Range '%s' is invalid, %u > %u", word, cpu_lower, cpu_upper);
+ else
+ for (cpu = cpu_lower; cpu <= cpu_upper; cpu++)
+ CPU_SET_S(cpu, CPU_ALLOC_SIZE(ncpus), c);
+ }
+
+ /* On success, sets *cpu_set and returns ncpus for the system. */
+ if (c) {
+ *cpu_set = c;
+ c = NULL;
+ }
+
+ return (int) ncpus;
+}
diff --git a/src/basic/cpu-set-util.h b/src/basic/cpu-set-util.h
new file mode 100644
index 0000000000..19b457a684
--- /dev/null
+++ b/src/basic/cpu-set-util.h
@@ -0,0 +1,34 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010-2015 Lennart Poettering
+ Copyright 2015 Filipe Brandenburger
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General 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 <sched.h>
+
+#include "macro.h"
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(cpu_set_t*, CPU_FREE);
+#define _cleanup_cpu_free_ _cleanup_(CPU_FREEp)
+
+cpu_set_t* cpu_set_malloc(unsigned *ncpus);
+
+int parse_cpu_set_and_warn(const char *rvalue, cpu_set_t **cpu_set, const char *unit, const char *filename, unsigned line, const char *lvalue);
diff --git a/src/basic/def.h b/src/basic/def.h
index 7c4161eb72..0657ac7367 100644
--- a/src/basic/def.h
+++ b/src/basic/def.h
@@ -35,17 +35,14 @@
* the watchdog pings will keep the loop busy. */
#define DEFAULT_EXIT_USEC (30*USEC_PER_SEC)
+/* The default value for the net.unix.max_dgram_qlen sysctl */
+#define DEFAULT_UNIX_MAX_DGRAM_QLEN 512UL
+
#define SYSTEMD_CGROUP_CONTROLLER "name=systemd"
#define SIGNALS_CRASH_HANDLER SIGSEGV,SIGILL,SIGFPE,SIGBUS,SIGQUIT,SIGABRT
#define SIGNALS_IGNORE SIGPIPE
-#define DIGITS "0123456789"
-#define LOWERCASE_LETTERS "abcdefghijklmnopqrstuvwxyz"
-#define UPPERCASE_LETTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-#define LETTERS LOWERCASE_LETTERS UPPERCASE_LETTERS
-#define ALPHANUMERICAL LETTERS DIGITS
-
#define REBOOT_PARAM_FILE "/run/systemd/reboot-param"
#ifdef HAVE_SPLIT_USR
@@ -78,3 +75,20 @@
#define NOTIFY_FD_MAX 768
#define NOTIFY_BUFFER_MAX PIPE_BUF
+
+#ifdef HAVE_SPLIT_USR
+#define _CONF_PATHS_SPLIT_USR(n) "/lib/" n "\0"
+#else
+#define _CONF_PATHS_SPLIT_USR(n)
+#endif
+
+/* Return a nulstr for a standard cascade of configuration paths,
+ * suitable to pass to conf_files_list_nulstr() or config_parse_many()
+ * to implement drop-in directories for extending configuration
+ * files. */
+#define CONF_PATHS_NULSTR(n) \
+ "/etc/" n "\0" \
+ "/run/" n "\0" \
+ "/usr/local/lib/" n "\0" \
+ "/usr/lib/" n "\0" \
+ _CONF_PATHS_SPLIT_USR(n)
diff --git a/src/basic/device-nodes.c b/src/basic/device-nodes.c
index 9d5af72d27..ec58cfdd4c 100644
--- a/src/basic/device-nodes.c
+++ b/src/basic/device-nodes.c
@@ -19,7 +19,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
#include <stdio.h>
+#include <string.h>
#include "device-nodes.h"
#include "utf8.h"
diff --git a/src/basic/device-nodes.h b/src/basic/device-nodes.h
index 04ba4897e5..9669c86970 100644
--- a/src/basic/device-nodes.h
+++ b/src/basic/device-nodes.h
@@ -21,5 +21,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <stddef.h>
+#include <sys/types.h>
+
int encode_devnode_name(const char *str, char *str_enc, size_t len);
int whitelisted_char_for_devnode(char c, const char *additional);
diff --git a/src/basic/dirent-util.c b/src/basic/dirent-util.c
new file mode 100644
index 0000000000..4ef5aba5a8
--- /dev/null
+++ b/src/basic/dirent-util.c
@@ -0,0 +1,80 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010-2012 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include "dirent-util.h"
+#include "path-util.h"
+#include "string-util.h"
+
+int dirent_ensure_type(DIR *d, struct dirent *de) {
+ struct stat st;
+
+ assert(d);
+ assert(de);
+
+ if (de->d_type != DT_UNKNOWN)
+ return 0;
+
+ if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0)
+ return -errno;
+
+ de->d_type =
+ S_ISREG(st.st_mode) ? DT_REG :
+ S_ISDIR(st.st_mode) ? DT_DIR :
+ S_ISLNK(st.st_mode) ? DT_LNK :
+ S_ISFIFO(st.st_mode) ? DT_FIFO :
+ S_ISSOCK(st.st_mode) ? DT_SOCK :
+ S_ISCHR(st.st_mode) ? DT_CHR :
+ S_ISBLK(st.st_mode) ? DT_BLK :
+ DT_UNKNOWN;
+
+ return 0;
+}
+
+bool dirent_is_file(const struct dirent *de) {
+ assert(de);
+
+ if (hidden_file(de->d_name))
+ return false;
+
+ if (de->d_type != DT_REG &&
+ de->d_type != DT_LNK &&
+ de->d_type != DT_UNKNOWN)
+ return false;
+
+ return true;
+}
+
+bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) {
+ assert(de);
+
+ if (de->d_type != DT_REG &&
+ de->d_type != DT_LNK &&
+ de->d_type != DT_UNKNOWN)
+ return false;
+
+ if (hidden_file_allow_backup(de->d_name))
+ return false;
+
+ return endswith(de->d_name, suffix);
+}
diff --git a/src/basic/dirent-util.h b/src/basic/dirent-util.h
new file mode 100644
index 0000000000..1ad5e4715a
--- /dev/null
+++ b/src/basic/dirent-util.h
@@ -0,0 +1,54 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dirent.h>
+#include <errno.h>
+#include <stdbool.h>
+
+#include "macro.h"
+#include "path-util.h"
+
+int dirent_ensure_type(DIR *d, struct dirent *de);
+
+bool dirent_is_file(const struct dirent *de) _pure_;
+bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) _pure_;
+
+#define FOREACH_DIRENT(de, d, on_error) \
+ for (errno = 0, de = readdir(d);; errno = 0, de = readdir(d)) \
+ if (!de) { \
+ if (errno > 0) { \
+ on_error; \
+ } \
+ break; \
+ } else if (hidden_file((de)->d_name)) \
+ continue; \
+ else
+
+#define FOREACH_DIRENT_ALL(de, d, on_error) \
+ for (errno = 0, de = readdir(d);; errno = 0, de = readdir(d)) \
+ if (!de) { \
+ if (errno > 0) { \
+ on_error; \
+ } \
+ break; \
+ } else
diff --git a/src/basic/env-util.c b/src/basic/env-util.c
index 4804a67f91..dd56545f12 100644
--- a/src/basic/env-util.c
+++ b/src/basic/env-util.c
@@ -19,14 +19,21 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
#include <limits.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
#include <unistd.h>
+#include "alloc-util.h"
+#include "env-util.h"
+#include "extract-word.h"
+#include "macro.h"
+#include "parse-util.h"
+#include "string-util.h"
#include "strv.h"
#include "utf8.h"
-#include "util.h"
-#include "env-util.h"
-#include "def.h"
#define VALID_CHARS_ENV_NAME \
DIGITS LETTERS \
@@ -135,6 +142,21 @@ bool strv_env_is_valid(char **e) {
return true;
}
+bool strv_env_name_is_valid(char **l) {
+ char **p, **q;
+
+ STRV_FOREACH(p, l) {
+ if (!env_name_is_valid(*p))
+ return false;
+
+ STRV_FOREACH(q, p + 1)
+ if (streq(*p, *q))
+ return false;
+ }
+
+ return true;
+}
+
bool strv_env_name_or_assignment_is_valid(char **l) {
char **p, **q;
@@ -541,7 +563,7 @@ char **replace_env_argv(char **argv, char **env) {
STRV_FOREACH(i, argv) {
/* If $FOO appears as single word, replace it by the split up variable */
- if ((*i)[0] == '$' && (*i)[1] != '{') {
+ if ((*i)[0] == '$' && (*i)[1] != '{' && (*i)[1] != '$') {
char *e;
char **w, **m = NULL;
unsigned q;
@@ -592,3 +614,13 @@ char **replace_env_argv(char **argv, char **env) {
ret[k] = NULL;
return ret;
}
+
+int getenv_bool(const char *p) {
+ const char *e;
+
+ e = getenv(p);
+ if (!e)
+ return -ENXIO;
+
+ return parse_boolean(e);
+}
diff --git a/src/basic/env-util.h b/src/basic/env-util.h
index 803aa61cad..3b83a63a78 100644
--- a/src/basic/env-util.h
+++ b/src/basic/env-util.h
@@ -22,6 +22,7 @@
***/
#include <stdbool.h>
+#include <stddef.h>
#include "macro.h"
@@ -36,6 +37,7 @@ 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);
+bool strv_env_name_is_valid(char **l);
bool strv_env_name_or_assignment_is_valid(char **l);
char **strv_env_merge(unsigned n_lists, ...);
@@ -47,3 +49,5 @@ char **strv_env_unset_many(char **l, ...) _sentinel_;
char *strv_env_get_n(char **l, const char *name, size_t k) _pure_;
char *strv_env_get(char **x, const char *n) _pure_;
+
+int getenv_bool(const char *p);
diff --git a/src/basic/errno-list.c b/src/basic/errno-list.c
index 34d1331486..b4d080103b 100644
--- a/src/basic/errno-list.c
+++ b/src/basic/errno-list.c
@@ -21,14 +21,14 @@
#include <string.h>
-#include "util.h"
#include "errno-list.h"
+#include "macro.h"
static const struct errno_name* lookup_errno(register const char *str,
- register unsigned int len);
+ register unsigned int len);
-#include "errno-to-name.h"
#include "errno-from-name.h"
+#include "errno-to-name.h"
const char *errno_to_name(int id) {
@@ -48,8 +48,9 @@ int errno_from_name(const char *name) {
sc = lookup_errno(name, strlen(name));
if (!sc)
- return 0;
+ return -EINVAL;
+ assert(sc->id > 0);
return sc->id;
}
diff --git a/src/basic/escape.c b/src/basic/escape.c
new file mode 100644
index 0000000000..5661f36813
--- /dev/null
+++ b/src/basic/escape.c
@@ -0,0 +1,476 @@
+/*-*- 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 <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "alloc-util.h"
+#include "escape.h"
+#include "hexdecoct.h"
+#include "macro.h"
+#include "utf8.h"
+
+size_t cescape_char(char c, char *buf) {
+ char * buf_old = buf;
+
+ switch (c) {
+
+ case '\a':
+ *(buf++) = '\\';
+ *(buf++) = 'a';
+ break;
+ case '\b':
+ *(buf++) = '\\';
+ *(buf++) = 'b';
+ break;
+ case '\f':
+ *(buf++) = '\\';
+ *(buf++) = 'f';
+ break;
+ case '\n':
+ *(buf++) = '\\';
+ *(buf++) = 'n';
+ break;
+ case '\r':
+ *(buf++) = '\\';
+ *(buf++) = 'r';
+ break;
+ case '\t':
+ *(buf++) = '\\';
+ *(buf++) = 't';
+ break;
+ case '\v':
+ *(buf++) = '\\';
+ *(buf++) = 'v';
+ break;
+ case '\\':
+ *(buf++) = '\\';
+ *(buf++) = '\\';
+ break;
+ case '"':
+ *(buf++) = '\\';
+ *(buf++) = '"';
+ break;
+ case '\'':
+ *(buf++) = '\\';
+ *(buf++) = '\'';
+ break;
+
+ default:
+ /* For special chars we prefer octal over
+ * hexadecimal encoding, simply because glib's
+ * g_strescape() does the same */
+ if ((c < ' ') || (c >= 127)) {
+ *(buf++) = '\\';
+ *(buf++) = octchar((unsigned char) c >> 6);
+ *(buf++) = octchar((unsigned char) c >> 3);
+ *(buf++) = octchar((unsigned char) c);
+ } else
+ *(buf++) = c;
+ break;
+ }
+
+ return buf - buf_old;
+}
+
+char *cescape_length(const char *s, size_t n) {
+ const char *f;
+ char *r, *t;
+
+ assert(s || n == 0);
+
+ /* Does C style string escaping. May be reversed with
+ * cunescape(). */
+
+ r = new(char, n*4 + 1);
+ if (!r)
+ return NULL;
+
+ for (f = s, t = r; f < s + n; f++)
+ t += cescape_char(*f, t);
+
+ *t = 0;
+
+ return r;
+}
+
+char *cescape(const char *s) {
+ assert(s);
+
+ return cescape_length(s, strlen(s));
+}
+
+int cunescape_one(const char *p, size_t length, uint32_t *ret, bool *eight_bit) {
+ int r = 1;
+
+ assert(p);
+ assert(*p);
+ assert(ret);
+
+ /* Unescapes C style. Returns the unescaped character in ret.
+ * Sets *eight_bit to true if the escaped sequence either fits in
+ * one byte in UTF-8 or is a non-unicode literal byte and should
+ * instead be copied directly.
+ */
+
+ 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 = (a << 4U) | b;
+ *eight_bit = true;
+ 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;
+
+ *ret = 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;
+
+ *ret = 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;
+ *eight_bit = true;
+ 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. */
+
+ pl = prefix ? strlen(prefix) : 0;
+
+ r = new(char, pl+length+1);
+ if (!r)
+ return -ENOMEM;
+
+ if (prefix)
+ memcpy(r, prefix, pl);
+
+ for (f = s, t = r + pl; f < s + length; f++) {
+ size_t remaining;
+ uint32_t u;
+ bool eight_bit = false;
+ int k;
+
+ remaining = s + length - f;
+ assert(remaining > 0);
+
+ if (*f != '\\') {
+ /* A literal literal, copy verbatim */
+ *(t++) = *f;
+ continue;
+ }
+
+ if (remaining == 1) {
+ if (flags & UNESCAPE_RELAX) {
+ /* A trailing backslash, copy verbatim */
+ *(t++) = *f;
+ continue;
+ }
+
+ free(r);
+ return -EINVAL;
+ }
+
+ k = cunescape_one(f + 1, remaining - 1, &u, &eight_bit);
+ if (k < 0) {
+ if (flags & UNESCAPE_RELAX) {
+ /* Invalid escape code, let's take it literal then */
+ *(t++) = '\\';
+ continue;
+ }
+
+ free(r);
+ return k;
+ }
+
+ f += k;
+ if (eight_bit)
+ /* One byte? Set directly as specified */
+ *(t++) = u;
+ else
+ /* Otherwise encode as multi-byte UTF-8 */
+ t += utf8_encode_unichar(t, u);
+ }
+
+ *t = 0;
+
+ *ret = r;
+ return t - r;
+}
+
+int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) {
+ return cunescape_length_with_prefix(s, length, NULL, flags, ret);
+}
+
+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) {
+ char *r, *t;
+ const char *f;
+
+ /* Escapes all chars in bad, in addition to \ and all special
+ * chars, in \xFF style escaping. May be reversed with
+ * cunescape(). */
+
+ r = new(char, strlen(s) * 4 + 1);
+ if (!r)
+ return NULL;
+
+ for (f = s, t = r; *f; f++) {
+
+ if ((*f < ' ') || (*f >= 127) ||
+ (*f == '\\') || strchr(bad, *f)) {
+ *(t++) = '\\';
+ *(t++) = 'x';
+ *(t++) = hexchar(*f >> 4);
+ *(t++) = hexchar(*f);
+ } else
+ *(t++) = *f;
+ }
+
+ *t = 0;
+
+ return r;
+}
+
+static char *strcpy_backslash_escaped(char *t, const char *s, const char *bad) {
+ assert(bad);
+
+ for (; *s; s++) {
+ if (*s == '\\' || strchr(bad, *s))
+ *(t++) = '\\';
+
+ *(t++) = *s;
+ }
+
+ return t;
+}
+
+char *shell_escape(const char *s, const char *bad) {
+ char *r, *t;
+
+ r = new(char, strlen(s)*2+1);
+ if (!r)
+ return NULL;
+
+ t = strcpy_backslash_escaped(r, s, bad);
+ *t = 0;
+
+ return r;
+}
+
+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);
+
+ t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE);
+
+ *(t++)= '"';
+ *t = 0;
+
+ return r;
+}
diff --git a/src/basic/escape.h b/src/basic/escape.h
new file mode 100644
index 0000000000..d943aa71f5
--- /dev/null
+++ b/src/basic/escape.h
@@ -0,0 +1,53 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "string-util.h"
+
+/* What characters are special in the shell? */
+/* must be escaped outside and inside double-quotes */
+#define SHELL_NEED_ESCAPE "\"\\`$"
+/* can be escaped or double-quoted */
+#define SHELL_NEED_QUOTES SHELL_NEED_ESCAPE GLOB_CHARS "'()<>|&;"
+
+typedef enum UnescapeFlags {
+ UNESCAPE_RELAX = 1,
+} UnescapeFlags;
+
+char *cescape(const char *s);
+char *cescape_length(const char *s, size_t n);
+size_t cescape_char(char c, char *buf);
+
+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);
+int cunescape_one(const char *p, size_t length, uint32_t *ret, bool *eight_bit);
+
+char *xescape(const char *s, const char *bad);
+
+char *shell_escape(const char *s, const char *bad);
+char *shell_maybe_quote(const char *s);
diff --git a/src/basic/ether-addr-util.c b/src/basic/ether-addr-util.c
new file mode 100644
index 0000000000..bc54f8f5f4
--- /dev/null
+++ b/src/basic/ether-addr-util.c
@@ -0,0 +1,46 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 Tom Gundersen
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General 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 <net/ethernet.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "ether-addr-util.h"
+#include "macro.h"
+
+char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]) {
+ assert(addr);
+ assert(buffer);
+
+ /* Like ether_ntoa() but uses %02x instead of %x to print
+ * ethernet addresses, which makes them look less funny. Also,
+ * doesn't use a static buffer. */
+
+ sprintf(buffer, "%02x:%02x:%02x:%02x:%02x:%02x",
+ addr->ether_addr_octet[0],
+ addr->ether_addr_octet[1],
+ addr->ether_addr_octet[2],
+ addr->ether_addr_octet[3],
+ addr->ether_addr_octet[4],
+ addr->ether_addr_octet[5]);
+
+ return buffer;
+}
diff --git a/src/basic/ether-addr-util.h b/src/basic/ether-addr-util.h
index 7033138788..008f3b893e 100644
--- a/src/basic/ether-addr-util.h
+++ b/src/basic/ether-addr-util.h
@@ -25,3 +25,7 @@
#define ETHER_ADDR_FORMAT_STR "%02X%02X%02X%02X%02X%02X"
#define ETHER_ADDR_FORMAT_VAL(x) (x).ether_addr_octet[0], (x).ether_addr_octet[1], (x).ether_addr_octet[2], (x).ether_addr_octet[3], (x).ether_addr_octet[4], (x).ether_addr_octet[5]
+
+#define ETHER_ADDR_TO_STRING_MAX (3*6)
+
+char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]);
diff --git a/src/basic/exit-status.c b/src/basic/exit-status.c
index fcff753ada..4c83731540 100644
--- a/src/basic/exit-status.c
+++ b/src/basic/exit-status.c
@@ -19,12 +19,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
#include <signal.h>
+#include <stdlib.h>
#include "exit-status.h"
-#include "set.h"
#include "macro.h"
+#include "set.h"
const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) {
diff --git a/src/basic/exit-status.h b/src/basic/exit-status.h
index 7259cd1d18..664222c1d6 100644
--- a/src/basic/exit-status.h
+++ b/src/basic/exit-status.h
@@ -23,6 +23,8 @@
#include <stdbool.h>
+#include "hashmap.h"
+#include "macro.h"
#include "set.h"
typedef enum ExitStatus {
diff --git a/src/basic/extract-word.c b/src/basic/extract-word.c
new file mode 100644
index 0000000000..090d2a7884
--- /dev/null
+++ b/src/basic/extract-word.c
@@ -0,0 +1,300 @@
+/*-*- 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 <errno.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "alloc-util.h"
+#include "escape.h"
+#include "extract-word.h"
+#include "log.h"
+#include "macro.h"
+#include "string-util.h"
+#include "utf8.h"
+
+int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags) {
+ _cleanup_free_ char *s = NULL;
+ size_t allocated = 0, sz = 0;
+ char c;
+ int r;
+
+ char quote = 0; /* 0 or ' or " */
+ bool backslash = false; /* whether we've just seen a backslash */
+
+ assert(p);
+ assert(ret);
+
+ /* Bail early if called after last value or with no input */
+ if (!*p)
+ goto finish_force_terminate;
+ c = **p;
+
+ if (!separators)
+ separators = WHITESPACE;
+
+ /* Parses the first word of a string, and returns it in
+ * *ret. Removes all quotes in the process. When parsing fails
+ * (because of an uneven number of quotes or similar), leaves
+ * the pointer *p at the first invalid character. */
+
+ if (flags & EXTRACT_DONT_COALESCE_SEPARATORS)
+ if (!GREEDY_REALLOC(s, allocated, sz+1))
+ return -ENOMEM;
+
+ for (;; (*p) ++, c = **p) {
+ if (c == 0)
+ goto finish_force_terminate;
+ else if (strchr(separators, c)) {
+ if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) {
+ (*p) ++;
+ goto finish_force_next;
+ }
+ } else {
+ /* We found a non-blank character, so we will always
+ * want to return a string (even if it is empty),
+ * allocate it here. */
+ if (!GREEDY_REALLOC(s, allocated, sz+1))
+ return -ENOMEM;
+ break;
+ }
+ }
+
+ for (;; (*p) ++, c = **p) {
+ if (backslash) {
+ if (!GREEDY_REALLOC(s, allocated, sz+7))
+ return -ENOMEM;
+
+ if (c == 0) {
+ if ((flags & EXTRACT_CUNESCAPE_RELAX) &&
+ (!quote || flags & EXTRACT_RELAX)) {
+ /* If we find an unquoted trailing backslash and we're in
+ * EXTRACT_CUNESCAPE_RELAX mode, keep it verbatim in the
+ * output.
+ *
+ * Unbalanced quotes will only be allowed in EXTRACT_RELAX
+ * mode, EXTRACT_CUNESCAPE_RELAX mode does not allow them.
+ */
+ s[sz++] = '\\';
+ goto finish_force_terminate;
+ }
+ if (flags & EXTRACT_RELAX)
+ goto finish_force_terminate;
+ return -EINVAL;
+ }
+
+ if (flags & EXTRACT_CUNESCAPE) {
+ uint32_t u;
+ bool eight_bit = false;
+
+ r = cunescape_one(*p, (size_t) -1, &u, &eight_bit);
+ if (r < 0) {
+ if (flags & EXTRACT_CUNESCAPE_RELAX) {
+ s[sz++] = '\\';
+ s[sz++] = c;
+ } else
+ return -EINVAL;
+ } else {
+ (*p) += r - 1;
+
+ if (eight_bit)
+ s[sz++] = u;
+ else
+ sz += utf8_encode_unichar(s + sz, u);
+ }
+ } else
+ s[sz++] = c;
+
+ backslash = false;
+
+ } else if (quote) { /* inside either single or double quotes */
+ for (;; (*p) ++, c = **p) {
+ if (c == 0) {
+ if (flags & EXTRACT_RELAX)
+ goto finish_force_terminate;
+ return -EINVAL;
+ } else if (c == quote) { /* found the end quote */
+ quote = 0;
+ break;
+ } else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) {
+ backslash = true;
+ break;
+ } else {
+ if (!GREEDY_REALLOC(s, allocated, sz+2))
+ return -ENOMEM;
+
+ s[sz++] = c;
+ }
+ }
+
+ } else {
+ for (;; (*p) ++, c = **p) {
+ if (c == 0)
+ goto finish_force_terminate;
+ else if ((c == '\'' || c == '"') && (flags & EXTRACT_QUOTES)) {
+ quote = c;
+ break;
+ } else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) {
+ backslash = true;
+ break;
+ } else if (strchr(separators, c)) {
+ if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) {
+ (*p) ++;
+ goto finish_force_next;
+ }
+ /* Skip additional coalesced separators. */
+ for (;; (*p) ++, c = **p) {
+ if (c == 0)
+ goto finish_force_terminate;
+ if (!strchr(separators, c))
+ break;
+ }
+ goto finish;
+
+ } else {
+ if (!GREEDY_REALLOC(s, allocated, sz+2))
+ return -ENOMEM;
+
+ s[sz++] = c;
+ }
+ }
+ }
+ }
+
+finish_force_terminate:
+ *p = NULL;
+finish:
+ if (!s) {
+ *p = NULL;
+ *ret = NULL;
+ return 0;
+ }
+
+finish_force_next:
+ s[sz] = 0;
+ *ret = s;
+ s = NULL;
+
+ return 1;
+}
+
+int extract_first_word_and_warn(
+ const char **p,
+ char **ret,
+ const char *separators,
+ ExtractFlags flags,
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *rvalue) {
+
+ /* Try to unquote it, if it fails, warn about it and try again
+ * but this time using EXTRACT_CUNESCAPE_RELAX to keep the
+ * backslashes verbatim in invalid escape sequences. */
+
+ const char *save;
+ int r;
+
+ save = *p;
+ r = extract_first_word(p, ret, separators, flags);
+ if (r >= 0)
+ return r;
+
+ if (r == -EINVAL && !(flags & EXTRACT_CUNESCAPE_RELAX)) {
+
+ /* Retry it with EXTRACT_CUNESCAPE_RELAX. */
+ *p = save;
+ r = extract_first_word(p, ret, separators, flags|EXTRACT_CUNESCAPE_RELAX);
+ if (r >= 0) {
+ /* It worked this time, hence it must have been an invalid escape sequence we could correct. */
+ log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Invalid escape sequences in line, correcting: \"%s\"", rvalue);
+ return r;
+ }
+
+ /* If it's still EINVAL; then it must be unbalanced quoting, report this. */
+ if (r == -EINVAL)
+ return log_syntax(unit, LOG_ERR, filename, line, r, "Unbalanced quoting, ignoring: \"%s\"", rvalue);
+ }
+
+ /* Can be any error, report it */
+ return log_syntax(unit, LOG_ERR, filename, line, r, "Unable to decode word \"%s\", ignoring: %m", rvalue);
+}
+
+int extract_many_words(const char **p, const char *separators, ExtractFlags flags, ...) {
+ va_list ap;
+ char **l;
+ int n = 0, i, c, r;
+
+ /* Parses a number of words from a string, stripping any
+ * quotes if necessary. */
+
+ assert(p);
+
+ /* Count how many words are expected */
+ va_start(ap, flags);
+ for (;;) {
+ if (!va_arg(ap, char **))
+ break;
+ n++;
+ }
+ va_end(ap);
+
+ if (n <= 0)
+ return 0;
+
+ /* Read all words into a temporary array */
+ l = newa0(char*, n);
+ for (c = 0; c < n; c++) {
+
+ r = extract_first_word(p, &l[c], separators, flags);
+ if (r < 0) {
+ int j;
+
+ for (j = 0; j < c; j++)
+ free(l[j]);
+
+ return r;
+ }
+
+ if (r == 0)
+ break;
+ }
+
+ /* If we managed to parse all words, return them in the passed
+ * in parameters */
+ va_start(ap, flags);
+ for (i = 0; i < n; i++) {
+ char **v;
+
+ v = va_arg(ap, char **);
+ assert(v);
+
+ *v = l[i];
+ }
+ va_end(ap);
+
+ return c;
+}
diff --git a/src/basic/extract-word.h b/src/basic/extract-word.h
new file mode 100644
index 0000000000..9606ab64b3
--- /dev/null
+++ b/src/basic/extract-word.h
@@ -0,0 +1,37 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "macro.h"
+
+typedef enum ExtractFlags {
+ EXTRACT_RELAX = 1,
+ EXTRACT_CUNESCAPE = 2,
+ EXTRACT_CUNESCAPE_RELAX = 4,
+ EXTRACT_QUOTES = 8,
+ EXTRACT_DONT_COALESCE_SEPARATORS = 16,
+ EXTRACT_RETAIN_ESCAPE = 32,
+} ExtractFlags;
+
+int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags);
+int extract_first_word_and_warn(const char **p, char **ret, const char *separators, ExtractFlags flags, const char *unit, const char *filename, unsigned line, const char *rvalue);
+int extract_many_words(const char **p, const char *separators, ExtractFlags flags, ...) _sentinel_;
diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c
new file mode 100644
index 0000000000..9759cac23c
--- /dev/null
+++ b/src/basic/fd-util.c
@@ -0,0 +1,360 @@
+/*-*- 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 <errno.h>
+#include <fcntl.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "fd-util.h"
+#include "macro.h"
+#include "missing.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "socket-util.h"
+#include "util.h"
+
+int close_nointr(int fd) {
+ assert(fd >= 0);
+
+ if (close(fd) >= 0)
+ return 0;
+
+ /*
+ * Just ignore EINTR; a retry loop is the wrong thing to do on
+ * Linux.
+ *
+ * http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html
+ * https://bugzilla.gnome.org/show_bug.cgi?id=682819
+ * http://utcc.utoronto.ca/~cks/space/blog/unix/CloseEINTR
+ * https://sites.google.com/site/michaelsafyan/software-engineering/checkforeintrwheninvokingclosethinkagain
+ */
+ if (errno == EINTR)
+ return 0;
+
+ return -errno;
+}
+
+int safe_close(int fd) {
+
+ /*
+ * Like close_nointr() but cannot fail. Guarantees errno is
+ * unchanged. Is a NOP with negative fds passed, and returns
+ * -1, so that it can be used in this syntax:
+ *
+ * fd = safe_close(fd);
+ */
+
+ if (fd >= 0) {
+ PROTECT_ERRNO;
+
+ /* The kernel might return pretty much any error code
+ * via close(), but the fd will be closed anyway. The
+ * only condition we want to check for here is whether
+ * the fd was invalid at all... */
+
+ assert_se(close_nointr(fd) != -EBADF);
+ }
+
+ return -1;
+}
+
+void safe_close_pair(int p[]) {
+ assert(p);
+
+ if (p[0] == p[1]) {
+ /* Special case pairs which use the same fd in both
+ * directions... */
+ p[0] = p[1] = safe_close(p[0]);
+ return;
+ }
+
+ p[0] = safe_close(p[0]);
+ p[1] = safe_close(p[1]);
+}
+
+void close_many(const int fds[], unsigned n_fd) {
+ unsigned i;
+
+ assert(fds || n_fd <= 0);
+
+ for (i = 0; i < n_fd; i++)
+ safe_close(fds[i]);
+}
+
+int fclose_nointr(FILE *f) {
+ assert(f);
+
+ /* Same as close_nointr(), but for fclose() */
+
+ if (fclose(f) == 0)
+ return 0;
+
+ if (errno == EINTR)
+ return 0;
+
+ return -errno;
+}
+
+FILE* safe_fclose(FILE *f) {
+
+ /* Same as safe_close(), but for fclose() */
+
+ if (f) {
+ PROTECT_ERRNO;
+
+ assert_se(fclose_nointr(f) != EBADF);
+ }
+
+ return NULL;
+}
+
+DIR* safe_closedir(DIR *d) {
+
+ if (d) {
+ PROTECT_ERRNO;
+
+ assert_se(closedir(d) >= 0 || errno != EBADF);
+ }
+
+ return NULL;
+}
+
+int fd_nonblock(int fd, bool nonblock) {
+ int flags, nflags;
+
+ assert(fd >= 0);
+
+ flags = fcntl(fd, F_GETFL, 0);
+ if (flags < 0)
+ return -errno;
+
+ if (nonblock)
+ nflags = flags | O_NONBLOCK;
+ else
+ nflags = flags & ~O_NONBLOCK;
+
+ if (nflags == flags)
+ return 0;
+
+ if (fcntl(fd, F_SETFL, nflags) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int fd_cloexec(int fd, bool cloexec) {
+ int flags, nflags;
+
+ assert(fd >= 0);
+
+ flags = fcntl(fd, F_GETFD, 0);
+ if (flags < 0)
+ return -errno;
+
+ if (cloexec)
+ nflags = flags | FD_CLOEXEC;
+ else
+ nflags = flags & ~FD_CLOEXEC;
+
+ if (nflags == flags)
+ return 0;
+
+ if (fcntl(fd, F_SETFD, nflags) < 0)
+ return -errno;
+
+ return 0;
+}
+
+_pure_ static bool fd_in_set(int fd, const int fdset[], unsigned n_fdset) {
+ unsigned i;
+
+ assert(n_fdset == 0 || fdset);
+
+ for (i = 0; i < n_fdset; i++)
+ if (fdset[i] == fd)
+ return true;
+
+ return false;
+}
+
+int close_all_fds(const int except[], unsigned n_except) {
+ _cleanup_closedir_ DIR *d = NULL;
+ struct dirent *de;
+ int r = 0;
+
+ assert(n_except == 0 || except);
+
+ d = opendir("/proc/self/fd");
+ if (!d) {
+ int fd;
+ struct rlimit rl;
+
+ /* When /proc isn't available (for example in chroots)
+ * the fallback is brute forcing through the fd
+ * table */
+
+ assert_se(getrlimit(RLIMIT_NOFILE, &rl) >= 0);
+ for (fd = 3; fd < (int) rl.rlim_max; fd ++) {
+
+ if (fd_in_set(fd, except, n_except))
+ continue;
+
+ if (close_nointr(fd) < 0)
+ if (errno != EBADF && r == 0)
+ r = -errno;
+ }
+
+ return r;
+ }
+
+ while ((de = readdir(d))) {
+ int fd = -1;
+
+ if (hidden_file(de->d_name))
+ continue;
+
+ if (safe_atoi(de->d_name, &fd) < 0)
+ /* Let's better ignore this, just in case */
+ continue;
+
+ if (fd < 3)
+ continue;
+
+ if (fd == dirfd(d))
+ continue;
+
+ if (fd_in_set(fd, except, n_except))
+ continue;
+
+ if (close_nointr(fd) < 0) {
+ /* Valgrind has its own FD and doesn't want to have it closed */
+ if (errno != EBADF && r == 0)
+ r = -errno;
+ }
+ }
+
+ return r;
+}
+
+int same_fd(int a, int b) {
+ struct stat sta, stb;
+ pid_t pid;
+ int r, fa, fb;
+
+ assert(a >= 0);
+ assert(b >= 0);
+
+ /* Compares two file descriptors. Note that semantics are
+ * quite different depending on whether we have kcmp() or we
+ * don't. If we have kcmp() this will only return true for
+ * dup()ed file descriptors, but not otherwise. If we don't
+ * have kcmp() this will also return true for two fds of the same
+ * file, created by separate open() calls. Since we use this
+ * call mostly for filtering out duplicates in the fd store
+ * this difference hopefully doesn't matter too much. */
+
+ if (a == b)
+ return true;
+
+ /* Try to use kcmp() if we have it. */
+ pid = getpid();
+ r = kcmp(pid, pid, KCMP_FILE, a, b);
+ if (r == 0)
+ return true;
+ if (r > 0)
+ return false;
+ if (errno != ENOSYS)
+ return -errno;
+
+ /* We don't have kcmp(), use fstat() instead. */
+ if (fstat(a, &sta) < 0)
+ return -errno;
+
+ if (fstat(b, &stb) < 0)
+ return -errno;
+
+ if ((sta.st_mode & S_IFMT) != (stb.st_mode & S_IFMT))
+ return false;
+
+ /* We consider all device fds different, since two device fds
+ * might refer to quite different device contexts even though
+ * they share the same inode and backing dev_t. */
+
+ if (S_ISCHR(sta.st_mode) || S_ISBLK(sta.st_mode))
+ return false;
+
+ if (sta.st_dev != stb.st_dev || sta.st_ino != stb.st_ino)
+ return false;
+
+ /* The fds refer to the same inode on disk, let's also check
+ * if they have the same fd flags. This is useful to
+ * distinguish the read and write side of a pipe created with
+ * pipe(). */
+ fa = fcntl(a, F_GETFL);
+ if (fa < 0)
+ return -errno;
+
+ fb = fcntl(b, F_GETFL);
+ if (fb < 0)
+ return -errno;
+
+ return fa == fb;
+}
+
+void cmsg_close_all(struct msghdr *mh) {
+ struct cmsghdr *cmsg;
+
+ assert(mh);
+
+ CMSG_FOREACH(cmsg, mh)
+ 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));
+}
+
+bool fdname_is_valid(const char *s) {
+ const char *p;
+
+ /* Validates a name for $LISTEN_FDNAMES. We basically allow
+ * everything ASCII that's not a control character. Also, as
+ * special exception the ":" character is not allowed, as we
+ * use that as field separator in $LISTEN_FDNAMES.
+ *
+ * Note that the empty string is explicitly allowed
+ * here. However, we limit the length of the names to 255
+ * characters. */
+
+ if (!s)
+ return false;
+
+ for (p = s; *p; p++) {
+ if (*p < ' ')
+ return false;
+ if (*p >= 127)
+ return false;
+ if (*p == ':')
+ return false;
+ }
+
+ return p - s < 256;
+}
diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h
new file mode 100644
index 0000000000..973413ff42
--- /dev/null
+++ b/src/basic/fd-util.h
@@ -0,0 +1,78 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dirent.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <sys/socket.h>
+
+#include "macro.h"
+
+/* 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)
+
+int close_nointr(int fd);
+int safe_close(int fd);
+void safe_close_pair(int p[]);
+
+void close_many(const int fds[], unsigned n_fd);
+
+int fclose_nointr(FILE *f);
+FILE* safe_fclose(FILE *f);
+DIR* safe_closedir(DIR *f);
+
+static inline void closep(int *fd) {
+ safe_close(*fd);
+}
+
+static inline void close_pairp(int (*p)[2]) {
+ safe_close_pair(*p);
+}
+
+static inline void fclosep(FILE **f) {
+ safe_fclose(*f);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, pclose);
+DEFINE_TRIVIAL_CLEANUP_FUNC(DIR*, closedir);
+
+#define _cleanup_close_ _cleanup_(closep)
+#define _cleanup_fclose_ _cleanup_(fclosep)
+#define _cleanup_pclose_ _cleanup_(pclosep)
+#define _cleanup_closedir_ _cleanup_(closedirp)
+#define _cleanup_close_pair_ _cleanup_(close_pairp)
+
+int fd_nonblock(int fd, bool nonblock);
+int fd_cloexec(int fd, bool cloexec);
+
+int close_all_fds(const int except[], unsigned n_except);
+
+int same_fd(int a, int b);
+
+void cmsg_close_all(struct msghdr *mh);
+
+bool fdname_is_valid(const char *s);
+
+#define ERRNO_IS_DISCONNECT(r) \
+ IN_SET(r, ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE)
diff --git a/src/basic/fdset.c b/src/basic/fdset.c
index d70fe156a2..de9b723ab8 100644
--- a/src/basic/fdset.c
+++ b/src/basic/fdset.c
@@ -19,28 +19,30 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <errno.h>
+#include <alloca.h>
#include <dirent.h>
+#include <errno.h>
#include <fcntl.h>
+#include <stddef.h>
-#include "set.h"
-#include "util.h"
-#include "macro.h"
-#include "fdset.h"
#include "sd-daemon.h"
+#include "fd-util.h"
+#include "fdset.h"
+#include "log.h"
+#include "macro.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "set.h"
+
#define MAKE_SET(s) ((Set*) s)
#define MAKE_FDSET(s) ((FDSet*) s)
-/* 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)
-
FDSet *fdset_new(void) {
return MAKE_FDSET(set_new(NULL));
}
-int fdset_new_array(FDSet **ret, int *fds, unsigned n_fds) {
+int fdset_new_array(FDSet **ret, const int *fds, unsigned n_fds) {
unsigned i;
FDSet *s;
int r;
diff --git a/src/basic/fdset.h b/src/basic/fdset.h
index 340438d7c4..615ba05661 100644
--- a/src/basic/fdset.h
+++ b/src/basic/fdset.h
@@ -21,6 +21,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <stdbool.h>
+
+#include "hashmap.h"
+#include "macro.h"
#include "set.h"
typedef struct FDSet FDSet;
@@ -35,7 +39,7 @@ int fdset_consume(FDSet *s, int fd);
bool fdset_contains(FDSet *s, int fd);
int fdset_remove(FDSet *s, int fd);
-int fdset_new_array(FDSet **ret, int *fds, unsigned n_fds);
+int fdset_new_array(FDSet **ret, const int *fds, unsigned n_fds);
int fdset_new_fill(FDSet **ret);
int fdset_new_listen_fds(FDSet **ret, bool unset);
diff --git a/src/basic/fileio-label.c b/src/basic/fileio-label.c
index f596f1d11f..1cee87c9cd 100644
--- a/src/basic/fileio-label.c
+++ b/src/basic/fileio-label.c
@@ -20,9 +20,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
-#include "selinux-util.h"
+#include <sys/stat.h>
+
#include "fileio-label.h"
+#include "fileio.h"
+#include "selinux-util.h"
int write_string_file_atomic_label(const char *fn, const char *line) {
int r;
diff --git a/src/basic/fileio-label.h b/src/basic/fileio-label.h
index 25fa351be2..9feb3cccb5 100644
--- a/src/basic/fileio-label.h
+++ b/src/basic/fileio-label.h
@@ -23,6 +23,7 @@
***/
#include <stdio.h>
+
#include "fileio.h"
int write_string_file_atomic_label(const char *fn, const char *line);
diff --git a/src/basic/fileio.c b/src/basic/fileio.c
index 4a9105f421..5ed5460904 100644
--- a/src/basic/fileio.c
+++ b/src/basic/fileio.c
@@ -19,13 +19,35 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
#include <unistd.h>
-#include "util.h"
-#include "strv.h"
-#include "utf8.h"
+#include "alloc-util.h"
#include "ctype.h"
+#include "escape.h"
+#include "fd-util.h"
#include "fileio.h"
+#include "fs-util.h"
+#include "hexdecoct.h"
+#include "log.h"
+#include "macro.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "random-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "time-util.h"
+#include "umask-util.h"
+#include "utf8.h"
int write_string_stream(FILE *f, const char *line, bool enforce_newline) {
@@ -51,7 +73,7 @@ static int write_string_file_atomic(const char *fn, const char *line, bool enfor
if (r < 0)
return r;
- fchmod_umask(fileno(f), 0644);
+ (void) fchmod_umask(fileno(f), 0644);
r = write_string_stream(f, line, enforce_newline);
if (r >= 0) {
@@ -60,13 +82,14 @@ static int write_string_file_atomic(const char *fn, const char *line, bool enfor
}
if (r < 0)
- unlink(p);
+ (void) unlink(p);
return r;
}
int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags) {
_cleanup_fclose_ FILE *f = NULL;
+ int q, r;
assert(fn);
assert(line);
@@ -74,30 +97,58 @@ int write_string_file(const char *fn, const char *line, WriteStringFileFlags fla
if (flags & WRITE_STRING_FILE_ATOMIC) {
assert(flags & WRITE_STRING_FILE_CREATE);
- return write_string_file_atomic(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE));
+ r = write_string_file_atomic(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE));
+ if (r < 0)
+ goto fail;
+
+ return r;
}
if (flags & WRITE_STRING_FILE_CREATE) {
f = fopen(fn, "we");
- if (!f)
- return -errno;
+ if (!f) {
+ r = -errno;
+ goto fail;
+ }
} else {
int fd;
/* We manually build our own version of fopen(..., "we") that
* works without O_CREAT */
fd = open(fn, O_WRONLY|O_CLOEXEC|O_NOCTTY);
- if (fd < 0)
- return -errno;
+ if (fd < 0) {
+ r = -errno;
+ goto fail;
+ }
f = fdopen(fd, "we");
if (!f) {
+ r = -errno;
safe_close(fd);
- return -errno;
+ goto fail;
}
}
- return write_string_stream(f, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE));
+ r = write_string_stream(f, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE));
+ if (r < 0)
+ goto fail;
+
+ return 0;
+
+fail:
+ if (!(flags & WRITE_STRING_FILE_VERIFY_ON_FAILURE))
+ return r;
+
+ f = safe_fclose(f);
+
+ /* OK, the operation failed, but let's see if the right
+ * contents in place already. If so, eat up the error. */
+
+ q = verify_file(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE));
+ if (q <= 0)
+ return r;
+
+ return 0;
}
int read_one_line_file(const char *fn, char **line) {
@@ -114,7 +165,7 @@ int read_one_line_file(const char *fn, char **line) {
if (!fgets(t, sizeof(t), f)) {
if (ferror(f))
- return errno ? -errno : -EIO;
+ return errno > 0 ? -errno : -EIO;
t[0] = 0;
}
@@ -128,15 +179,41 @@ int read_one_line_file(const char *fn, char **line) {
return 0;
}
-int verify_one_line_file(const char *fn, const char *line) {
- _cleanup_free_ char *value = NULL;
- int r;
+int verify_file(const char *fn, const char *blob, bool accept_extra_nl) {
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_free_ char *buf = NULL;
+ size_t l, k;
- r = read_one_line_file(fn, &value);
- if (r < 0)
- return r;
+ assert(fn);
+ assert(blob);
+
+ l = strlen(blob);
- return streq(value, line);
+ if (accept_extra_nl && endswith(blob, "\n"))
+ accept_extra_nl = false;
+
+ buf = malloc(l + accept_extra_nl + 1);
+ if (!buf)
+ return -ENOMEM;
+
+ f = fopen(fn, "re");
+ if (!f)
+ return -errno;
+
+ /* We try to read one byte more than we need, so that we know whether we hit eof */
+ errno = 0;
+ k = fread(buf, 1, l + accept_extra_nl + 1, f);
+ if (ferror(f))
+ return errno > 0 ? -errno : -EIO;
+
+ if (k != l && k != l + accept_extra_nl)
+ return 0;
+ if (memcmp(buf, blob, l) != 0)
+ return 0;
+ if (k > l && buf[l] != '\n')
+ return 0;
+
+ return 1;
}
int read_full_stream(FILE *f, char **contents, size_t *size) {
@@ -775,15 +852,19 @@ int executable_is_script(const char *path, char **interpreter) {
/**
* Retrieve one field from a file like /proc/self/status. pattern
- * should start with '\n' and end with a ':'. Whitespace and zeros
- * after the ':' will be skipped. field must be freed afterwards.
+ * should not include whitespace or the delimiter (':'). pattern matches only
+ * the beginning of a line. Whitespace before ':' is skipped. Whitespace and
+ * zeros after the ':' will be skipped. field must be freed afterwards.
+ * terminator specifies the terminating characters of the field value (not
+ * included in the value).
*/
-int get_status_field(const char *filename, const char *pattern, char **field) {
+int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field) {
_cleanup_free_ char *status = NULL;
char *t, *f;
size_t len;
int r;
+ assert(terminator);
assert(filename);
assert(pattern);
assert(field);
@@ -792,11 +873,31 @@ int get_status_field(const char *filename, const char *pattern, char **field) {
if (r < 0)
return r;
- t = strstr(status, pattern);
- if (!t)
- return -ENOENT;
+ t = status;
+
+ do {
+ bool pattern_ok;
+
+ do {
+ t = strstr(t, pattern);
+ if (!t)
+ return -ENOENT;
+
+ /* Check that pattern occurs in beginning of line. */
+ pattern_ok = (t == status || t[-1] == '\n');
+
+ t += strlen(pattern);
+
+ } while (!pattern_ok);
+
+ t += strspn(t, " \t");
+ if (!*t)
+ return -ENOENT;
+
+ } while (*t != ':');
+
+ t++;
- t += strlen(pattern);
if (*t) {
t += strspn(t, " \t");
@@ -812,7 +913,7 @@ int get_status_field(const char *filename, const char *pattern, char **field) {
t --;
}
- len = strcspn(t, WHITESPACE);
+ len = strcspn(t, terminator);
f = strndup(t, len);
if (!f)
@@ -821,3 +922,332 @@ int get_status_field(const char *filename, const char *pattern, char **field) {
*field = f;
return 0;
}
+
+DIR *xopendirat(int fd, const char *name, int flags) {
+ int nfd;
+ DIR *d;
+
+ assert(!(flags & O_CREAT));
+
+ nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags, 0);
+ if (nfd < 0)
+ return NULL;
+
+ d = fdopendir(nfd);
+ if (!d) {
+ safe_close(nfd);
+ return NULL;
+ }
+
+ return d;
+}
+
+static int search_and_fopen_internal(const char *path, const char *mode, const char *root, char **search, FILE **_f) {
+ char **i;
+
+ assert(path);
+ assert(mode);
+ assert(_f);
+
+ if (!path_strv_resolve_uniq(search, root))
+ return -ENOMEM;
+
+ STRV_FOREACH(i, search) {
+ _cleanup_free_ char *p = NULL;
+ FILE *f;
+
+ if (root)
+ p = strjoin(root, *i, "/", path, NULL);
+ else
+ p = strjoin(*i, "/", path, NULL);
+ if (!p)
+ return -ENOMEM;
+
+ f = fopen(p, mode);
+ if (f) {
+ *_f = f;
+ return 0;
+ }
+
+ if (errno != ENOENT)
+ return -errno;
+ }
+
+ return -ENOENT;
+}
+
+int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f) {
+ _cleanup_strv_free_ char **copy = NULL;
+
+ assert(path);
+ assert(mode);
+ assert(_f);
+
+ if (path_is_absolute(path)) {
+ FILE *f;
+
+ f = fopen(path, mode);
+ if (f) {
+ *_f = f;
+ return 0;
+ }
+
+ return -errno;
+ }
+
+ copy = strv_copy((char**) search);
+ if (!copy)
+ return -ENOMEM;
+
+ return search_and_fopen_internal(path, mode, root, copy, _f);
+}
+
+int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f) {
+ _cleanup_strv_free_ char **s = NULL;
+
+ if (path_is_absolute(path)) {
+ FILE *f;
+
+ f = fopen(path, mode);
+ if (f) {
+ *_f = f;
+ return 0;
+ }
+
+ return -errno;
+ }
+
+ s = strv_split_nulstr(search);
+ if (!s)
+ return -ENOMEM;
+
+ return search_and_fopen_internal(path, mode, root, s, _f);
+}
+
+int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
+ FILE *f;
+ char *t;
+ int r, fd;
+
+ assert(path);
+ assert(_f);
+ assert(_temp_path);
+
+ r = tempfn_xxxxxx(path, NULL, &t);
+ if (r < 0)
+ return r;
+
+ fd = mkostemp_safe(t, O_WRONLY|O_CLOEXEC);
+ if (fd < 0) {
+ free(t);
+ return -errno;
+ }
+
+ f = fdopen(fd, "we");
+ if (!f) {
+ unlink_noerrno(t);
+ free(t);
+ safe_close(fd);
+ return -errno;
+ }
+
+ *_f = f;
+ *_temp_path = t;
+
+ return 0;
+}
+
+int fflush_and_check(FILE *f) {
+ assert(f);
+
+ errno = 0;
+ fflush(f);
+
+ if (ferror(f))
+ return errno > 0 ? -errno : -EIO;
+
+ return 0;
+}
+
+/* This is much like like mkostemp() but is subject to umask(). */
+int mkostemp_safe(char *pattern, int flags) {
+ _cleanup_umask_ mode_t u;
+ int fd;
+
+ assert(pattern);
+
+ u = umask(077);
+
+ fd = mkostemp(pattern, flags);
+ if (fd < 0)
+ return -errno;
+
+ return fd;
+}
+
+int open_tmpfile(const char *path, int flags) {
+ char *p;
+ int fd;
+
+ assert(path);
+
+#ifdef O_TMPFILE
+ /* Try O_TMPFILE first, if it is supported */
+ fd = open(path, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR);
+ if (fd >= 0)
+ return fd;
+#endif
+
+ /* Fall back to unguessable name + unlinking */
+ p = strjoina(path, "/systemd-tmp-XXXXXX");
+
+ fd = mkostemp_safe(p, flags);
+ if (fd < 0)
+ return fd;
+
+ unlink(p);
+ return fd;
+}
+
+int tempfn_xxxxxx(const char *p, const char *extra, char **ret) {
+ const char *fn;
+ char *t;
+
+ assert(p);
+ assert(ret);
+
+ /*
+ * Turns this:
+ * /foo/bar/waldo
+ *
+ * Into this:
+ * /foo/bar/.#<extra>waldoXXXXXX
+ */
+
+ fn = basename(p);
+ if (!filename_is_valid(fn))
+ return -EINVAL;
+
+ if (extra == NULL)
+ extra = "";
+
+ t = new(char, strlen(p) + 2 + strlen(extra) + 6 + 1);
+ if (!t)
+ return -ENOMEM;
+
+ strcpy(stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn), "XXXXXX");
+
+ *ret = path_kill_slashes(t);
+ return 0;
+}
+
+int tempfn_random(const char *p, const char *extra, char **ret) {
+ const char *fn;
+ char *t, *x;
+ uint64_t u;
+ unsigned i;
+
+ assert(p);
+ assert(ret);
+
+ /*
+ * Turns this:
+ * /foo/bar/waldo
+ *
+ * Into this:
+ * /foo/bar/.#<extra>waldobaa2a261115984a9
+ */
+
+ fn = basename(p);
+ if (!filename_is_valid(fn))
+ return -EINVAL;
+
+ if (!extra)
+ extra = "";
+
+ t = new(char, strlen(p) + 2 + strlen(extra) + 16 + 1);
+ if (!t)
+ return -ENOMEM;
+
+ x = stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn);
+
+ u = random_u64();
+ for (i = 0; i < 16; i++) {
+ *(x++) = hexchar(u & 0xF);
+ u >>= 4;
+ }
+
+ *x = 0;
+
+ *ret = path_kill_slashes(t);
+ return 0;
+}
+
+int tempfn_random_child(const char *p, const char *extra, char **ret) {
+ char *t, *x;
+ uint64_t u;
+ unsigned i;
+
+ assert(p);
+ assert(ret);
+
+ /* Turns this:
+ * /foo/bar/waldo
+ * Into this:
+ * /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0
+ */
+
+ if (!extra)
+ extra = "";
+
+ t = new(char, strlen(p) + 3 + strlen(extra) + 16 + 1);
+ if (!t)
+ return -ENOMEM;
+
+ x = stpcpy(stpcpy(stpcpy(t, p), "/.#"), extra);
+
+ u = random_u64();
+ for (i = 0; i < 16; i++) {
+ *(x++) = hexchar(u & 0xF);
+ u >>= 4;
+ }
+
+ *x = 0;
+
+ *ret = path_kill_slashes(t);
+ return 0;
+}
+
+int write_timestamp_file_atomic(const char *fn, usec_t n) {
+ char ln[DECIMAL_STR_MAX(n)+2];
+
+ /* Creates a "timestamp" file, that contains nothing but a
+ * usec_t timestamp, formatted in ASCII. */
+
+ if (n <= 0 || n >= USEC_INFINITY)
+ return -ERANGE;
+
+ xsprintf(ln, USEC_FMT "\n", n);
+
+ return write_string_file(fn, ln, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
+}
+
+int read_timestamp_file(const char *fn, usec_t *ret) {
+ _cleanup_free_ char *ln = NULL;
+ uint64_t t;
+ int r;
+
+ r = read_one_line_file(fn, &ln);
+ if (r < 0)
+ return r;
+
+ r = safe_atou64(ln, &t);
+ if (r < 0)
+ return r;
+
+ if (t <= 0 || t >= (uint64_t) USEC_INFINITY)
+ return -ERANGE;
+
+ *ret = (usec_t) t;
+ return 0;
+}
diff --git a/src/basic/fileio.h b/src/basic/fileio.h
index 2e8148ff24..95e8698941 100644
--- a/src/basic/fileio.h
+++ b/src/basic/fileio.h
@@ -20,15 +20,21 @@
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+
+#include <dirent.h>
+#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
+#include <sys/types.h>
#include "macro.h"
+#include "time-util.h"
typedef enum {
WRITE_STRING_FILE_CREATE = 1,
WRITE_STRING_FILE_ATOMIC = 2,
WRITE_STRING_FILE_AVOID_NEWLINE = 4,
+ WRITE_STRING_FILE_VERIFY_ON_FAILURE = 8,
} WriteStringFileFlags;
int write_string_stream(FILE *f, const char *line, bool enforce_newline);
@@ -38,7 +44,7 @@ int read_one_line_file(const char *fn, char **line);
int read_full_file(const char *fn, char **contents, size_t *size);
int read_full_stream(FILE *f, char **contents, size_t *size);
-int verify_one_line_file(const char *fn, const char *line);
+int verify_file(const char *fn, const char *blob, bool accept_extra_nl);
int parse_env_file(const char *fname, const char *separator, ...) _sentinel_;
int load_env_file(FILE *f, const char *fname, const char *separator, char ***l);
@@ -48,4 +54,31 @@ int write_env_file(const char *fname, char **l);
int executable_is_script(const char *path, char **interpreter);
-int get_status_field(const char *filename, const char *pattern, char **field);
+int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field);
+
+DIR *xopendirat(int dirfd, const char *name, int flags);
+
+int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f);
+int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f);
+
+#define FOREACH_LINE(line, f, on_error) \
+ for (;;) \
+ if (!fgets(line, sizeof(line), f)) { \
+ if (ferror(f)) { \
+ on_error; \
+ } \
+ break; \
+ } else
+
+int fflush_and_check(FILE *f);
+
+int fopen_temporary(const char *path, FILE **_f, char **_temp_path);
+int mkostemp_safe(char *pattern, int flags);
+int open_tmpfile(const char *path, int flags);
+
+int tempfn_xxxxxx(const char *p, const char *extra, char **ret);
+int tempfn_random(const char *p, const char *extra, char **ret);
+int tempfn_random_child(const char *p, const char *extra, char **ret);
+
+int write_timestamp_file_atomic(const char *fn, usec_t n);
+int read_timestamp_file(const char *fn, usec_t *ret);
diff --git a/src/shared/formats-util.h b/src/basic/formats-util.h
index ce516b117d..ce516b117d 100644
--- a/src/shared/formats-util.h
+++ b/src/basic/formats-util.h
diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c
new file mode 100644
index 0000000000..d31bd6e273
--- /dev/null
+++ b/src/basic/fs-util.c
@@ -0,0 +1,514 @@
+/*-*- 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 <dirent.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "alloc-util.h"
+#include "dirent-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "log.h"
+#include "macro.h"
+#include "missing.h"
+#include "mkdir.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "time-util.h"
+#include "user-util.h"
+#include "util.h"
+
+int unlink_noerrno(const char *path) {
+ PROTECT_ERRNO;
+ int r;
+
+ r = unlink(path);
+ if (r < 0)
+ return -errno;
+
+ return 0;
+}
+
+int rmdir_parents(const char *path, const char *stop) {
+ size_t l;
+ int r = 0;
+
+ assert(path);
+ assert(stop);
+
+ l = strlen(path);
+
+ /* Skip trailing slashes */
+ while (l > 0 && path[l-1] == '/')
+ l--;
+
+ while (l > 0) {
+ char *t;
+
+ /* Skip last component */
+ while (l > 0 && path[l-1] != '/')
+ l--;
+
+ /* Skip trailing slashes */
+ while (l > 0 && path[l-1] == '/')
+ l--;
+
+ if (l <= 0)
+ break;
+
+ t = strndup(path, l);
+ if (!t)
+ return -ENOMEM;
+
+ if (path_startswith(stop, t)) {
+ free(t);
+ return 0;
+ }
+
+ r = rmdir(t);
+ free(t);
+
+ if (r < 0)
+ if (errno != ENOENT)
+ return -errno;
+ }
+
+ return 0;
+}
+
+
+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;
+
+ /* renameat2() exists since Linux 3.15, btrfs added support for it later.
+ * If it is not implemented, fallback to another method. */
+ if (!IN_SET(errno, EINVAL, ENOSYS))
+ 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;
+}
+
+int readlinkat_malloc(int fd, const char *p, char **ret) {
+ size_t l = 100;
+ int r;
+
+ assert(p);
+ assert(ret);
+
+ for (;;) {
+ char *c;
+ ssize_t n;
+
+ c = new(char, l);
+ if (!c)
+ return -ENOMEM;
+
+ n = readlinkat(fd, p, c, l-1);
+ if (n < 0) {
+ r = -errno;
+ free(c);
+ return r;
+ }
+
+ if ((size_t) n < l-1) {
+ c[n] = 0;
+ *ret = c;
+ return 0;
+ }
+
+ free(c);
+ l *= 2;
+ }
+}
+
+int readlink_malloc(const char *p, char **ret) {
+ return readlinkat_malloc(AT_FDCWD, p, ret);
+}
+
+int readlink_value(const char *p, char **ret) {
+ _cleanup_free_ char *link = NULL;
+ char *value;
+ int r;
+
+ r = readlink_malloc(p, &link);
+ if (r < 0)
+ return r;
+
+ value = basename(link);
+ if (!value)
+ return -ENOENT;
+
+ value = strdup(value);
+ if (!value)
+ return -ENOMEM;
+
+ *ret = value;
+
+ return 0;
+}
+
+int readlink_and_make_absolute(const char *p, char **r) {
+ _cleanup_free_ char *target = NULL;
+ char *k;
+ int j;
+
+ assert(p);
+ assert(r);
+
+ j = readlink_malloc(p, &target);
+ if (j < 0)
+ return j;
+
+ k = file_in_same_dir(p, target);
+ if (!k)
+ return -ENOMEM;
+
+ *r = k;
+ return 0;
+}
+
+int readlink_and_canonicalize(const char *p, char **r) {
+ char *t, *s;
+ int j;
+
+ assert(p);
+ assert(r);
+
+ j = readlink_and_make_absolute(p, &t);
+ if (j < 0)
+ return j;
+
+ s = canonicalize_file_name(t);
+ if (s) {
+ free(t);
+ *r = s;
+ } else
+ *r = t;
+
+ path_kill_slashes(*r);
+
+ return 0;
+}
+
+int readlink_and_make_absolute_root(const char *root, const char *path, char **ret) {
+ _cleanup_free_ char *target = NULL, *t = NULL;
+ const char *full;
+ int r;
+
+ full = prefix_roota(root, path);
+ r = readlink_malloc(full, &target);
+ if (r < 0)
+ return r;
+
+ t = file_in_same_dir(path, target);
+ if (!t)
+ return -ENOMEM;
+
+ *ret = t;
+ t = NULL;
+
+ return 0;
+}
+
+int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
+ assert(path);
+
+ /* Under the assumption that we are running privileged we
+ * first change the access mode and only then hand out
+ * ownership to avoid a window where access is too open. */
+
+ if (mode != MODE_INVALID)
+ if (chmod(path, mode) < 0)
+ return -errno;
+
+ if (uid != UID_INVALID || gid != GID_INVALID)
+ if (chown(path, uid, gid) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid) {
+ assert(fd >= 0);
+
+ /* Under the assumption that we are running privileged we
+ * first change the access mode and only then hand out
+ * ownership to avoid a window where access is too open. */
+
+ if (mode != MODE_INVALID)
+ if (fchmod(fd, mode) < 0)
+ return -errno;
+
+ if (uid != UID_INVALID || gid != GID_INVALID)
+ if (fchown(fd, uid, gid) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int fchmod_umask(int fd, mode_t m) {
+ mode_t u;
+ int r;
+
+ u = umask(0777);
+ r = fchmod(fd, m & (~u)) < 0 ? -errno : 0;
+ umask(u);
+
+ return r;
+}
+
+int fd_warn_permissions(const char *path, int fd) {
+ struct stat st;
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (st.st_mode & 0111)
+ log_warning("Configuration file %s is marked executable. Please remove executable permission bits. Proceeding anyway.", path);
+
+ if (st.st_mode & 0002)
+ log_warning("Configuration file %s is marked world-writable. Please remove world writability permission bits. Proceeding anyway.", path);
+
+ if (getpid() == 1 && (st.st_mode & 0044) != 0044)
+ log_warning("Configuration file %s is marked world-inaccessible. This has no effect as configuration data is accessible via APIs without restrictions. Proceeding anyway.", path);
+
+ return 0;
+}
+
+int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) {
+ _cleanup_close_ int fd;
+ int r;
+
+ assert(path);
+
+ if (parents)
+ mkdir_parents(path, 0755);
+
+ fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode > 0 ? mode : 0644);
+ if (fd < 0)
+ return -errno;
+
+ if (mode != MODE_INVALID) {
+ r = fchmod(fd, mode);
+ if (r < 0)
+ return -errno;
+ }
+
+ if (uid != UID_INVALID || gid != GID_INVALID) {
+ r = fchown(fd, uid, gid);
+ if (r < 0)
+ return -errno;
+ }
+
+ if (stamp != USEC_INFINITY) {
+ struct timespec ts[2];
+
+ timespec_store(&ts[0], stamp);
+ ts[1] = ts[0];
+ r = futimens(fd, ts);
+ } else
+ r = futimens(fd, NULL);
+ if (r < 0)
+ return -errno;
+
+ return 0;
+}
+
+int touch(const char *path) {
+ return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID);
+}
+
+int symlink_idempotent(const char *from, const char *to) {
+ _cleanup_free_ char *p = NULL;
+ int r;
+
+ assert(from);
+ assert(to);
+
+ if (symlink(from, to) < 0) {
+ if (errno != EEXIST)
+ return -errno;
+
+ r = readlink_malloc(to, &p);
+ if (r < 0)
+ return r;
+
+ if (!streq(p, from))
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int symlink_atomic(const char *from, const char *to) {
+ _cleanup_free_ char *t = NULL;
+ int r;
+
+ assert(from);
+ assert(to);
+
+ r = tempfn_random(to, NULL, &t);
+ if (r < 0)
+ return r;
+
+ if (symlink(from, t) < 0)
+ return -errno;
+
+ if (rename(t, to) < 0) {
+ unlink_noerrno(t);
+ return -errno;
+ }
+
+ return 0;
+}
+
+int mknod_atomic(const char *path, mode_t mode, dev_t dev) {
+ _cleanup_free_ char *t = NULL;
+ int r;
+
+ assert(path);
+
+ r = tempfn_random(path, NULL, &t);
+ if (r < 0)
+ return r;
+
+ if (mknod(t, mode, dev) < 0)
+ return -errno;
+
+ if (rename(t, path) < 0) {
+ unlink_noerrno(t);
+ return -errno;
+ }
+
+ return 0;
+}
+
+int mkfifo_atomic(const char *path, mode_t mode) {
+ _cleanup_free_ char *t = NULL;
+ int r;
+
+ assert(path);
+
+ r = tempfn_random(path, NULL, &t);
+ if (r < 0)
+ return r;
+
+ if (mkfifo(t, mode) < 0)
+ return -errno;
+
+ if (rename(t, path) < 0) {
+ unlink_noerrno(t);
+ return -errno;
+ }
+
+ return 0;
+}
+
+int get_files_in_directory(const char *path, char ***list) {
+ _cleanup_closedir_ DIR *d = NULL;
+ size_t bufsize = 0, n = 0;
+ _cleanup_strv_free_ char **l = NULL;
+
+ assert(path);
+
+ /* Returns all files in a directory in *list, and the number
+ * of files as return value. If list is NULL returns only the
+ * number. */
+
+ d = opendir(path);
+ if (!d)
+ return -errno;
+
+ for (;;) {
+ struct dirent *de;
+
+ errno = 0;
+ de = readdir(d);
+ if (!de && errno > 0)
+ return -errno;
+ if (!de)
+ break;
+
+ dirent_ensure_type(d, de);
+
+ if (!dirent_is_file(de))
+ continue;
+
+ if (list) {
+ /* one extra slot is needed for the terminating NULL */
+ if (!GREEDY_REALLOC(l, bufsize, n + 2))
+ return -ENOMEM;
+
+ l[n] = strdup(de->d_name);
+ if (!l[n])
+ return -ENOMEM;
+
+ l[++n] = NULL;
+ } else
+ n++;
+ }
+
+ if (list) {
+ *list = l;
+ l = NULL; /* avoid freeing */
+ }
+
+ return n;
+}
diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h
new file mode 100644
index 0000000000..67ed214b51
--- /dev/null
+++ b/src/basic/fs-util.h
@@ -0,0 +1,77 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <fcntl.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/inotify.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "time-util.h"
+
+int unlink_noerrno(const char *path);
+
+int rmdir_parents(const char *path, const char *stop);
+
+int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath);
+
+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 readlink_and_make_absolute_root(const char *root, const char *path, char **ret);
+
+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 fchmod_umask(int fd, mode_t mode);
+
+int fd_warn_permissions(const char *path, int fd);
+
+#define laccess(path, mode) faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW)
+
+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);
+
+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);
+int mkfifo_atomic(const char *path, mode_t mode);
+
+int get_files_in_directory(const char *path, char ***list);
+
+#define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX + 1)
+
+#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \
+ for ((e) = &buffer.ev; \
+ (uint8_t*) (e) < (uint8_t*) (buffer.raw) + (sz); \
+ (e) = (struct inotify_event*) ((uint8_t*) (e) + sizeof(struct inotify_event) + (e)->len))
+
+union inotify_event_buffer {
+ struct inotify_event ev;
+ uint8_t raw[INOTIFY_EVENT_MAX];
+};
diff --git a/src/basic/glob-util.c b/src/basic/glob-util.c
new file mode 100644
index 0000000000..811ab6ec36
--- /dev/null
+++ b/src/basic/glob-util.c
@@ -0,0 +1,72 @@
+/*-*- 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 <errno.h>
+#include <glob.h>
+
+#include "glob-util.h"
+#include "macro.h"
+#include "strv.h"
+
+int glob_exists(const char *path) {
+ _cleanup_globfree_ glob_t g = {};
+ int k;
+
+ assert(path);
+
+ errno = 0;
+ k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
+
+ if (k == GLOB_NOMATCH)
+ return 0;
+ if (k == GLOB_NOSPACE)
+ return -ENOMEM;
+ if (k != 0)
+ return errno > 0 ? -errno : -EIO;
+
+ return !strv_isempty(g.gl_pathv);
+}
+
+int glob_extend(char ***strv, const char *path) {
+ _cleanup_globfree_ glob_t g = {};
+ int k;
+ char **p;
+
+ errno = 0;
+ k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
+
+ if (k == GLOB_NOMATCH)
+ return -ENOENT;
+ if (k == GLOB_NOSPACE)
+ return -ENOMEM;
+ if (k != 0)
+ return errno > 0 ? -errno : -EIO;
+ if (strv_isempty(g.gl_pathv))
+ return -ENOENT;
+
+ STRV_FOREACH(p, g.gl_pathv) {
+ k = strv_extend(strv, *p);
+ if (k < 0)
+ return k;
+ }
+
+ return 0;
+}
diff --git a/src/basic/glob-util.h b/src/basic/glob-util.h
new file mode 100644
index 0000000000..a0a5efe5b6
--- /dev/null
+++ b/src/basic/glob-util.h
@@ -0,0 +1,38 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+#include <string.h>
+
+#include "macro.h"
+#include "string-util.h"
+
+int glob_exists(const char *path);
+int glob_extend(char ***strv, const char *path);
+
+#define _cleanup_globfree_ _cleanup_(globfree)
+
+_pure_ static inline bool string_is_glob(const char *p) {
+ /* Check if a string contains any glob patterns. */
+ return !!strpbrk(p, GLOB_CHARS);
+}
diff --git a/src/basic/gunicode.c b/src/basic/gunicode.c
index d89a2f3ed9..542110503f 100644
--- a/src/basic/gunicode.c
+++ b/src/basic/gunicode.c
@@ -4,6 +4,8 @@
* Copyright 2000, 2005 Red Hat, Inc.
*/
+#include <stdlib.h>
+
#include "gunicode.h"
#define unichar uint32_t
diff --git a/src/basic/gunicode.h b/src/basic/gunicode.h
index e70818fdd7..b03aa43160 100644
--- a/src/basic/gunicode.h
+++ b/src/basic/gunicode.h
@@ -6,8 +6,8 @@
#pragma once
-#include <stdint.h>
#include <stdbool.h>
+#include <stdint.h>
#include <stdlib.h>
char *utf8_prev_char (const char *p);
diff --git a/src/basic/hash-funcs.c b/src/basic/hash-funcs.c
new file mode 100644
index 0000000000..d4affaffee
--- /dev/null
+++ b/src/basic/hash-funcs.c
@@ -0,0 +1,83 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+ Copyright 2014 Michal Schmidt
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General 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 "hash-funcs.h"
+
+void string_hash_func(const void *p, struct siphash *state) {
+ siphash24_compress(p, strlen(p) + 1, state);
+}
+
+int string_compare_func(const void *a, const void *b) {
+ return strcmp(a, b);
+}
+
+const struct hash_ops string_hash_ops = {
+ .hash = string_hash_func,
+ .compare = string_compare_func
+};
+
+void trivial_hash_func(const void *p, struct siphash *state) {
+ siphash24_compress(&p, sizeof(p), state);
+}
+
+int trivial_compare_func(const void *a, const void *b) {
+ return a < b ? -1 : (a > b ? 1 : 0);
+}
+
+const struct hash_ops trivial_hash_ops = {
+ .hash = trivial_hash_func,
+ .compare = trivial_compare_func
+};
+
+void uint64_hash_func(const void *p, struct siphash *state) {
+ siphash24_compress(p, sizeof(uint64_t), state);
+}
+
+int uint64_compare_func(const void *_a, const void *_b) {
+ uint64_t a, b;
+ a = *(const uint64_t*) _a;
+ b = *(const uint64_t*) _b;
+ return a < b ? -1 : (a > b ? 1 : 0);
+}
+
+const struct hash_ops uint64_hash_ops = {
+ .hash = uint64_hash_func,
+ .compare = uint64_compare_func
+};
+
+#if SIZEOF_DEV_T != 8
+void devt_hash_func(const void *p, struct siphash *state) {
+ siphash24_compress(p, sizeof(dev_t), state);
+}
+
+int devt_compare_func(const void *_a, const void *_b) {
+ dev_t a, b;
+ a = *(const dev_t*) _a;
+ b = *(const dev_t*) _b;
+ return a < b ? -1 : (a > b ? 1 : 0);
+}
+
+const struct hash_ops devt_hash_ops = {
+ .hash = devt_hash_func,
+ .compare = devt_compare_func
+};
+#endif
diff --git a/src/basic/hash-funcs.h b/src/basic/hash-funcs.h
new file mode 100644
index 0000000000..c640eaf4d1
--- /dev/null
+++ b/src/basic/hash-funcs.h
@@ -0,0 +1,67 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+ Copyright 2014 Michal Schmidt
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General 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 "siphash24.h"
+
+typedef void (*hash_func_t)(const void *p, struct siphash *state);
+typedef int (*compare_func_t)(const void *a, const void *b);
+
+struct hash_ops {
+ hash_func_t hash;
+ compare_func_t compare;
+};
+
+void string_hash_func(const void *p, struct siphash *state);
+int string_compare_func(const void *a, const void *b) _pure_;
+extern const struct hash_ops string_hash_ops;
+
+/* This will compare the passed pointers directly, and will not
+ * dereference them. This is hence not useful for strings or
+ * suchlike. */
+void trivial_hash_func(const void *p, struct siphash *state);
+int trivial_compare_func(const void *a, const void *b) _const_;
+extern const struct hash_ops trivial_hash_ops;
+
+/* 32bit values we can always just embed in the pointer itself, but
+ * in order to support 32bit archs we need store 64bit values
+ * indirectly, since they don't fit in a pointer. */
+void uint64_hash_func(const void *p, struct siphash *state);
+int uint64_compare_func(const void *a, const void *b) _pure_;
+extern const struct hash_ops uint64_hash_ops;
+
+/* On some archs dev_t is 32bit, and on others 64bit. And sometimes
+ * it's 64bit on 32bit archs, and sometimes 32bit on 64bit archs. Yuck! */
+#if SIZEOF_DEV_T != 8
+void devt_hash_func(const void *p, struct siphash *state) _pure_;
+int devt_compare_func(const void *a, const void *b) _pure_;
+extern const struct hash_ops devt_hash_ops = {
+ .hash = devt_hash_func,
+ .compare = devt_compare_func
+};
+#else
+#define devt_hash_func uint64_hash_func
+#define devt_compare_func uint64_compare_func
+#define devt_hash_ops uint64_hash_ops
+#endif
diff --git a/src/basic/hashmap.c b/src/basic/hashmap.c
index 7d2a4160c6..dcd8ae412d 100644
--- a/src/basic/hashmap.c
+++ b/src/basic/hashmap.c
@@ -20,20 +20,24 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
#include <errno.h>
-#include <pthread.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
-#include "util.h"
+#include "alloc-util.h"
#include "hashmap.h"
-#include "set.h"
#include "macro.h"
-#include "siphash24.h"
-#include "strv.h"
#include "mempool.h"
+#include "process-util.h"
#include "random-util.h"
+#include "set.h"
+#include "siphash24.h"
+#include "strv.h"
+#include "util.h"
#ifdef ENABLE_DEBUG_HASHMAP
+#include <pthread.h>
#include "list.h"
#endif
@@ -276,74 +280,6 @@ static const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = {
},
};
-unsigned long string_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
- uint64_t u;
- siphash24((uint8_t*) &u, p, strlen(p), hash_key);
- return (unsigned long) u;
-}
-
-int string_compare_func(const void *a, const void *b) {
- return strcmp(a, b);
-}
-
-const struct hash_ops string_hash_ops = {
- .hash = string_hash_func,
- .compare = string_compare_func
-};
-
-unsigned long trivial_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
- uint64_t u;
- siphash24((uint8_t*) &u, &p, sizeof(p), hash_key);
- return (unsigned long) u;
-}
-
-int trivial_compare_func(const void *a, const void *b) {
- return a < b ? -1 : (a > b ? 1 : 0);
-}
-
-const struct hash_ops trivial_hash_ops = {
- .hash = trivial_hash_func,
- .compare = trivial_compare_func
-};
-
-unsigned long uint64_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
- uint64_t u;
- siphash24((uint8_t*) &u, p, sizeof(uint64_t), hash_key);
- return (unsigned long) u;
-}
-
-int uint64_compare_func(const void *_a, const void *_b) {
- uint64_t a, b;
- a = *(const uint64_t*) _a;
- b = *(const uint64_t*) _b;
- return a < b ? -1 : (a > b ? 1 : 0);
-}
-
-const struct hash_ops uint64_hash_ops = {
- .hash = uint64_hash_func,
- .compare = uint64_compare_func
-};
-
-#if SIZEOF_DEV_T != 8
-unsigned long devt_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
- uint64_t u;
- siphash24((uint8_t*) &u, p, sizeof(dev_t), hash_key);
- return (unsigned long) u;
-}
-
-int devt_compare_func(const void *_a, const void *_b) {
- dev_t a, b;
- a = *(const dev_t*) _a;
- b = *(const dev_t*) _b;
- return a < b ? -1 : (a > b ? 1 : 0);
-}
-
-const struct hash_ops devt_hash_ops = {
- .hash = devt_hash_func,
- .compare = devt_compare_func
-};
-#endif
-
static unsigned n_buckets(HashmapBase *h) {
return h->has_indirect ? h->indirect.n_buckets
: hashmap_type_info[h->type].n_direct_buckets;
@@ -379,7 +315,16 @@ static uint8_t *hash_key(HashmapBase *h) {
}
static unsigned base_bucket_hash(HashmapBase *h, const void *p) {
- return (unsigned) (h->hash_ops->hash(p, hash_key(h)) % n_buckets(h));
+ struct siphash state;
+ uint64_t hash;
+
+ siphash24_init(&state, hash_key(h));
+
+ h->hash_ops->hash(p, &state);
+
+ hash = siphash24_finalize(&state);
+
+ return (unsigned) (hash % n_buckets(h));
}
#define bucket_hash(h, p) base_bucket_hash(HASHMAP_BASE(h), p)
diff --git a/src/basic/hashmap.h b/src/basic/hashmap.h
index 2af23024de..fdba9c61ff 100644
--- a/src/basic/hashmap.h
+++ b/src/basic/hashmap.h
@@ -22,8 +22,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <limits.h>
#include <stdbool.h>
+#include <stddef.h>
+#include "hash-funcs.h"
#include "macro.h"
#include "util.h"
@@ -67,47 +70,6 @@ typedef struct {
#define _IDX_ITERATOR_FIRST (UINT_MAX - 1)
#define ITERATOR_FIRST ((Iterator) { .idx = _IDX_ITERATOR_FIRST, .next_key = NULL })
-typedef unsigned long (*hash_func_t)(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]);
-typedef int (*compare_func_t)(const void *a, const void *b);
-
-struct hash_ops {
- hash_func_t hash;
- compare_func_t compare;
-};
-
-unsigned long string_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) _pure_;
-int string_compare_func(const void *a, const void *b) _pure_;
-extern const struct hash_ops string_hash_ops;
-
-/* This will compare the passed pointers directly, and will not
- * dereference them. This is hence not useful for strings or
- * suchlike. */
-unsigned long trivial_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) _pure_;
-int trivial_compare_func(const void *a, const void *b) _const_;
-extern const struct hash_ops trivial_hash_ops;
-
-/* 32bit values we can always just embedd in the pointer itself, but
- * in order to support 32bit archs we need store 64bit values
- * indirectly, since they don't fit in a pointer. */
-unsigned long uint64_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) _pure_;
-int uint64_compare_func(const void *a, const void *b) _pure_;
-extern const struct hash_ops uint64_hash_ops;
-
-/* On some archs dev_t is 32bit, and on others 64bit. And sometimes
- * it's 64bit on 32bit archs, and sometimes 32bit on 64bit archs. Yuck! */
-#if SIZEOF_DEV_T != 8
-unsigned long devt_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) _pure_;
-int devt_compare_func(const void *a, const void *b) _pure_;
-extern const struct hash_ops devt_hash_ops = {
- .hash = devt_hash_func,
- .compare = devt_compare_func
-};
-#else
-#define devt_hash_func uint64_hash_func
-#define devt_compare_func uint64_compare_func
-#define devt_hash_ops uint64_hash_ops
-#endif
-
/* Macros for type checking */
#define PTR_COMPATIBLE_WITH_HASHMAP_BASE(h) \
(__builtin_types_compatible_p(typeof(h), HashmapBase*) || \
diff --git a/src/basic/hexdecoct.c b/src/basic/hexdecoct.c
new file mode 100644
index 0000000000..1e907de228
--- /dev/null
+++ b/src/basic/hexdecoct.c
@@ -0,0 +1,700 @@
+/*-*- 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 <ctype.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "alloc-util.h"
+#include "hexdecoct.h"
+#include "macro.h"
+
+char octchar(int x) {
+ return '0' + (x & 7);
+}
+
+int unoctchar(char c) {
+
+ if (c >= '0' && c <= '7')
+ return c - '0';
+
+ return -EINVAL;
+}
+
+char decchar(int x) {
+ return '0' + (x % 10);
+}
+
+int undecchar(char c) {
+
+ if (c >= '0' && c <= '9')
+ return c - '0';
+
+ return -EINVAL;
+}
+
+char hexchar(int x) {
+ static const char table[16] = "0123456789abcdef";
+
+ return table[x & 15];
+}
+
+int unhexchar(char c) {
+
+ if (c >= '0' && c <= '9')
+ return c - '0';
+
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+
+ return -EINVAL;
+}
+
+char *hexmem(const void *p, size_t l) {
+ char *r, *z;
+ const uint8_t *x;
+
+ z = r = malloc(l * 2 + 1);
+ if (!r)
+ return NULL;
+
+ for (x = p; x < (const uint8_t*) p + l; x++) {
+ *(z++) = hexchar(*x >> 4);
+ *(z++) = hexchar(*x & 15);
+ }
+
+ *z = 0;
+ return r;
+}
+
+int unhexmem(const char *p, size_t l, void **mem, size_t *len) {
+ _cleanup_free_ uint8_t *r = NULL;
+ uint8_t *z;
+ const char *x;
+
+ assert(mem);
+ assert(len);
+ assert(p);
+
+ z = r = malloc((l + 1) / 2 + 1);
+ if (!r)
+ return -ENOMEM;
+
+ for (x = p; x < p + l; x += 2) {
+ int a, b;
+
+ a = unhexchar(x[0]);
+ if (a < 0)
+ return a;
+ else if (x+1 < p + l) {
+ b = unhexchar(x[1]);
+ if (b < 0)
+ return b;
+ } else
+ b = 0;
+
+ *(z++) = (uint8_t) a << 4 | (uint8_t) b;
+ }
+
+ *z = 0;
+
+ *mem = r;
+ r = NULL;
+ *len = (l + 1) / 2;
+
+ return 0;
+}
+
+/* https://tools.ietf.org/html/rfc4648#section-6
+ * Notice that base32hex differs from base32 in the alphabet it uses.
+ * The distinction is that the base32hex representation preserves the
+ * order of the underlying data when compared as bytestrings, this is
+ * useful when representing NSEC3 hashes, as one can then verify the
+ * order of hashes directly from their representation. */
+char base32hexchar(int x) {
+ static const char table[32] = "0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUV";
+
+ return table[x & 31];
+}
+
+int unbase32hexchar(char c) {
+ unsigned offset;
+
+ if (c >= '0' && c <= '9')
+ return c - '0';
+
+ offset = '9' - '0' + 1;
+
+ if (c >= 'A' && c <= 'V')
+ return c - 'A' + offset;
+
+ return -EINVAL;
+}
+
+char *base32hexmem(const void *p, size_t l, bool padding) {
+ char *r, *z;
+ const uint8_t *x;
+ size_t len;
+
+ if (padding)
+ /* five input bytes makes eight output bytes, padding is added so we must round up */
+ len = 8 * (l + 4) / 5;
+ else {
+ /* same, but round down as there is no padding */
+ len = 8 * l / 5;
+
+ switch (l % 5) {
+ case 4:
+ len += 7;
+ break;
+ case 3:
+ len += 5;
+ break;
+ case 2:
+ len += 4;
+ break;
+ case 1:
+ len += 2;
+ break;
+ }
+ }
+
+ z = r = malloc(len + 1);
+ if (!r)
+ return NULL;
+
+ for (x = p; x < (const uint8_t*) p + (l / 5) * 5; x += 5) {
+ /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ
+ x[3] == QQQQQQQQ; x[4] == WWWWWWWW */
+ *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */
+ *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */
+ *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */
+ *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */
+ *(z++) = base32hexchar((x[2] & 15) << 1 | x[3] >> 7); /* 000ZZZZQ */
+ *(z++) = base32hexchar((x[3] & 127) >> 2); /* 000QQQQQ */
+ *(z++) = base32hexchar((x[3] & 3) << 3 | x[4] >> 5); /* 000QQWWW */
+ *(z++) = base32hexchar((x[4] & 31)); /* 000WWWWW */
+ }
+
+ switch (l % 5) {
+ case 4:
+ *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */
+ *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */
+ *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */
+ *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */
+ *(z++) = base32hexchar((x[2] & 15) << 1 | x[3] >> 7); /* 000ZZZZQ */
+ *(z++) = base32hexchar((x[3] & 127) >> 2); /* 000QQQQQ */
+ *(z++) = base32hexchar((x[3] & 3) << 3); /* 000QQ000 */
+ if (padding)
+ *(z++) = '=';
+
+ break;
+
+ case 3:
+ *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */
+ *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */
+ *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */
+ *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */
+ *(z++) = base32hexchar((x[2] & 15) << 1); /* 000ZZZZ0 */
+ if (padding) {
+ *(z++) = '=';
+ *(z++) = '=';
+ *(z++) = '=';
+ }
+
+ break;
+
+ case 2:
+ *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */
+ *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */
+ *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */
+ *(z++) = base32hexchar((x[1] & 1) << 4); /* 000Y0000 */
+ if (padding) {
+ *(z++) = '=';
+ *(z++) = '=';
+ *(z++) = '=';
+ *(z++) = '=';
+ }
+
+ break;
+
+ case 1:
+ *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */
+ *(z++) = base32hexchar((x[0] & 7) << 2); /* 000XXX00 */
+ if (padding) {
+ *(z++) = '=';
+ *(z++) = '=';
+ *(z++) = '=';
+ *(z++) = '=';
+ *(z++) = '=';
+ *(z++) = '=';
+ }
+
+ break;
+ }
+
+ *z = 0;
+ return r;
+}
+
+int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *_len) {
+ _cleanup_free_ uint8_t *r = NULL;
+ int a, b, c, d, e, f, g, h;
+ uint8_t *z;
+ const char *x;
+ size_t len;
+ unsigned pad = 0;
+
+ assert(p);
+
+ /* padding ensures any base32hex input has input divisible by 8 */
+ if (padding && l % 8 != 0)
+ return -EINVAL;
+
+ if (padding) {
+ /* strip the padding */
+ while (l > 0 && p[l - 1] == '=' && pad < 7) {
+ pad ++;
+ l --;
+ }
+ }
+
+ /* a group of eight input bytes needs five output bytes, in case of
+ padding we need to add some extra bytes */
+ len = (l / 8) * 5;
+
+ switch (l % 8) {
+ case 7:
+ len += 4;
+ break;
+ case 5:
+ len += 3;
+ break;
+ case 4:
+ len += 2;
+ break;
+ case 2:
+ len += 1;
+ break;
+ case 0:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ z = r = malloc(len + 1);
+ if (!r)
+ return -ENOMEM;
+
+ for (x = p; x < p + (l / 8) * 8; x += 8) {
+ /* a == 000XXXXX; b == 000YYYYY; c == 000ZZZZZ; d == 000WWWWW
+ e == 000SSSSS; f == 000QQQQQ; g == 000VVVVV; h == 000RRRRR */
+ a = unbase32hexchar(x[0]);
+ if (a < 0)
+ return -EINVAL;
+
+ b = unbase32hexchar(x[1]);
+ if (b < 0)
+ return -EINVAL;
+
+ c = unbase32hexchar(x[2]);
+ if (c < 0)
+ return -EINVAL;
+
+ d = unbase32hexchar(x[3]);
+ if (d < 0)
+ return -EINVAL;
+
+ e = unbase32hexchar(x[4]);
+ if (e < 0)
+ return -EINVAL;
+
+ f = unbase32hexchar(x[5]);
+ if (f < 0)
+ return -EINVAL;
+
+ g = unbase32hexchar(x[6]);
+ if (g < 0)
+ return -EINVAL;
+
+ h = unbase32hexchar(x[7]);
+ if (h < 0)
+ return -EINVAL;
+
+ *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */
+ *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
+ *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1; /* WWWWSSSS */
+ *(z++) = (uint8_t) e << 7 | (uint8_t) f << 2 | (uint8_t) g >> 3; /* SQQQQQVV */
+ *(z++) = (uint8_t) g << 5 | (uint8_t) h; /* VVVRRRRR */
+ }
+
+ switch (l % 8) {
+ case 7:
+ a = unbase32hexchar(x[0]);
+ if (a < 0)
+ return -EINVAL;
+
+ b = unbase32hexchar(x[1]);
+ if (b < 0)
+ return -EINVAL;
+
+ c = unbase32hexchar(x[2]);
+ if (c < 0)
+ return -EINVAL;
+
+ d = unbase32hexchar(x[3]);
+ if (d < 0)
+ return -EINVAL;
+
+ e = unbase32hexchar(x[4]);
+ if (e < 0)
+ return -EINVAL;
+
+ f = unbase32hexchar(x[5]);
+ if (f < 0)
+ return -EINVAL;
+
+ g = unbase32hexchar(x[6]);
+ if (g < 0)
+ return -EINVAL;
+
+ /* g == 000VV000 */
+ if (g & 7)
+ return -EINVAL;
+
+ *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */
+ *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
+ *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1; /* WWWWSSSS */
+ *(z++) = (uint8_t) e << 7 | (uint8_t) f << 2 | (uint8_t) g >> 3; /* SQQQQQVV */
+
+ break;
+ case 5:
+ a = unbase32hexchar(x[0]);
+ if (a < 0)
+ return -EINVAL;
+
+ b = unbase32hexchar(x[1]);
+ if (b < 0)
+ return -EINVAL;
+
+ c = unbase32hexchar(x[2]);
+ if (c < 0)
+ return -EINVAL;
+
+ d = unbase32hexchar(x[3]);
+ if (d < 0)
+ return -EINVAL;
+
+ e = unbase32hexchar(x[4]);
+ if (e < 0)
+ return -EINVAL;
+
+ /* e == 000SSSS0 */
+ if (e & 1)
+ return -EINVAL;
+
+ *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */
+ *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
+ *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1; /* WWWWSSSS */
+
+ break;
+ case 4:
+ a = unbase32hexchar(x[0]);
+ if (a < 0)
+ return -EINVAL;
+
+ b = unbase32hexchar(x[1]);
+ if (b < 0)
+ return -EINVAL;
+
+ c = unbase32hexchar(x[2]);
+ if (c < 0)
+ return -EINVAL;
+
+ d = unbase32hexchar(x[3]);
+ if (d < 0)
+ return -EINVAL;
+
+ /* d == 000W0000 */
+ if (d & 15)
+ return -EINVAL;
+
+ *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */
+ *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
+
+ break;
+ case 2:
+ a = unbase32hexchar(x[0]);
+ if (a < 0)
+ return -EINVAL;
+
+ b = unbase32hexchar(x[1]);
+ if (b < 0)
+ return -EINVAL;
+
+ /* b == 000YYY00 */
+ if (b & 3)
+ return -EINVAL;
+
+ *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */
+
+ break;
+ case 0:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *z = 0;
+
+ *mem = r;
+ r = NULL;
+ *_len = len;
+
+ return 0;
+}
+
+/* https://tools.ietf.org/html/rfc4648#section-4 */
+char base64char(int x) {
+ static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/";
+ return table[x & 63];
+}
+
+int unbase64char(char c) {
+ unsigned offset;
+
+ if (c >= 'A' && c <= 'Z')
+ return c - 'A';
+
+ offset = 'Z' - 'A' + 1;
+
+ if (c >= 'a' && c <= 'z')
+ return c - 'a' + offset;
+
+ offset += 'z' - 'a' + 1;
+
+ if (c >= '0' && c <= '9')
+ return c - '0' + offset;
+
+ offset += '9' - '0' + 1;
+
+ if (c == '+')
+ return offset;
+
+ offset ++;
+
+ if (c == '/')
+ return offset;
+
+ return -EINVAL;
+}
+
+char *base64mem(const void *p, size_t l) {
+ char *r, *z;
+ const uint8_t *x;
+
+ /* three input bytes makes four output bytes, padding is added so we must round up */
+ z = r = malloc(4 * (l + 2) / 3 + 1);
+ if (!r)
+ return NULL;
+
+ for (x = p; x < (const uint8_t*) p + (l / 3) * 3; x += 3) {
+ /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ */
+ *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */
+ *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */
+ *(z++) = base64char((x[1] & 15) << 2 | x[2] >> 6); /* 00YYYYZZ */
+ *(z++) = base64char(x[2] & 63); /* 00ZZZZZZ */
+ }
+
+ switch (l % 3) {
+ case 2:
+ *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */
+ *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */
+ *(z++) = base64char((x[1] & 15) << 2); /* 00YYYY00 */
+ *(z++) = '=';
+
+ break;
+ case 1:
+ *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */
+ *(z++) = base64char((x[0] & 3) << 4); /* 00XX0000 */
+ *(z++) = '=';
+ *(z++) = '=';
+
+ break;
+ }
+
+ *z = 0;
+ return r;
+}
+
+int unbase64mem(const char *p, size_t l, void **mem, size_t *_len) {
+ _cleanup_free_ uint8_t *r = NULL;
+ int a, b, c, d;
+ uint8_t *z;
+ const char *x;
+ size_t len;
+
+ assert(p);
+
+ /* padding ensures any base63 input has input divisible by 4 */
+ if (l % 4 != 0)
+ return -EINVAL;
+
+ /* strip the padding */
+ if (l > 0 && p[l - 1] == '=')
+ l --;
+ if (l > 0 && p[l - 1] == '=')
+ l --;
+
+ /* a group of four input bytes needs three output bytes, in case of
+ padding we need to add two or three extra bytes */
+ len = (l / 4) * 3 + (l % 4 ? (l % 4) - 1 : 0);
+
+ z = r = malloc(len + 1);
+ if (!r)
+ return -ENOMEM;
+
+ for (x = p; x < p + (l / 4) * 4; x += 4) {
+ /* a == 00XXXXXX; b == 00YYYYYY; c == 00ZZZZZZ; d == 00WWWWWW */
+ a = unbase64char(x[0]);
+ if (a < 0)
+ return -EINVAL;
+
+ b = unbase64char(x[1]);
+ if (b < 0)
+ return -EINVAL;
+
+ c = unbase64char(x[2]);
+ if (c < 0)
+ return -EINVAL;
+
+ d = unbase64char(x[3]);
+ if (d < 0)
+ return -EINVAL;
+
+ *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
+ *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
+ *(z++) = (uint8_t) c << 6 | (uint8_t) d; /* ZZWWWWWW */
+ }
+
+ switch (l % 4) {
+ case 3:
+ a = unbase64char(x[0]);
+ if (a < 0)
+ return -EINVAL;
+
+ b = unbase64char(x[1]);
+ if (b < 0)
+ return -EINVAL;
+
+ c = unbase64char(x[2]);
+ if (c < 0)
+ return -EINVAL;
+
+ /* c == 00ZZZZ00 */
+ if (c & 3)
+ return -EINVAL;
+
+ *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
+ *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
+
+ break;
+ case 2:
+ a = unbase64char(x[0]);
+ if (a < 0)
+ return -EINVAL;
+
+ b = unbase64char(x[1]);
+ if (b < 0)
+ return -EINVAL;
+
+ /* b == 00YY0000 */
+ if (b & 15)
+ return -EINVAL;
+
+ *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */
+
+ break;
+ case 0:
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *z = 0;
+
+ *mem = r;
+ r = NULL;
+ *_len = len;
+
+ return 0;
+}
+
+void hexdump(FILE *f, const void *p, size_t s) {
+ const uint8_t *b = p;
+ unsigned n = 0;
+
+ assert(s == 0 || b);
+
+ while (s > 0) {
+ size_t i;
+
+ fprintf(f, "%04x ", n);
+
+ for (i = 0; i < 16; i++) {
+
+ if (i >= s)
+ fputs(" ", f);
+ else
+ fprintf(f, "%02x ", b[i]);
+
+ if (i == 7)
+ fputc(' ', f);
+ }
+
+ fputc(' ', f);
+
+ for (i = 0; i < 16; i++) {
+
+ if (i >= s)
+ fputc(' ', f);
+ else
+ fputc(isprint(b[i]) ? (char) b[i] : '.', f);
+ }
+
+ fputc('\n', f);
+
+ if (s < 16)
+ break;
+
+ n += 16;
+ b += 16;
+ s -= 16;
+ }
+}
diff --git a/src/basic/hexdecoct.h b/src/basic/hexdecoct.h
new file mode 100644
index 0000000000..d9eb54a8a1
--- /dev/null
+++ b/src/basic/hexdecoct.h
@@ -0,0 +1,55 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "macro.h"
+
+char octchar(int x) _const_;
+int unoctchar(char c) _const_;
+
+char decchar(int x) _const_;
+int undecchar(char c) _const_;
+
+char hexchar(int x) _const_;
+int unhexchar(char c) _const_;
+
+char *hexmem(const void *p, size_t l);
+int unhexmem(const char *p, size_t l, void **mem, size_t *len);
+
+char base32hexchar(int x) _const_;
+int unbase32hexchar(char c) _const_;
+
+char base64char(int x) _const_;
+int unbase64char(char c) _const_;
+
+char *base32hexmem(const void *p, size_t l, bool padding);
+int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *len);
+
+char *base64mem(const void *p, size_t l);
+int unbase64mem(const char *p, size_t l, void **mem, size_t *len);
+
+void hexdump(FILE *f, const void *p, size_t s);
diff --git a/src/basic/hostname-util.c b/src/basic/hostname-util.c
index 1b816fb77a..795afb6d00 100644
--- a/src/basic/hostname-util.c
+++ b/src/basic/hostname-util.c
@@ -19,11 +19,19 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <bits/local_lim.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
#include <sys/utsname.h>
-#include <ctype.h>
+#include <unistd.h>
-#include "util.h"
+#include "fd-util.h"
+#include "fileio.h"
#include "hostname-util.h"
+#include "macro.h"
+#include "string-util.h"
bool hostname_is_set(void) {
struct utsname u;
@@ -69,7 +77,7 @@ static bool hostname_valid_char(char c) {
* allow_trailing_dot is true and at least two components are present
* in the name. Note that due to the restricted charset and length
* this call is substantially more conservative than
- * dns_domain_is_valid().
+ * dns_name_is_valid().
*/
bool hostname_is_valid(const char *s, bool allow_trailing_dot) {
unsigned n_dots = 0;
diff --git a/src/basic/in-addr-util.c b/src/basic/in-addr-util.c
index d88864b598..8609ffb3c9 100644
--- a/src/basic/in-addr-util.c
+++ b/src/basic/in-addr-util.c
@@ -20,8 +20,15 @@
***/
#include <arpa/inet.h>
+#include <endian.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include "alloc-util.h"
#include "in-addr-util.h"
+#include "macro.h"
+#include "util.h"
int in_addr_is_null(int family, const union in_addr_union *u) {
assert(u);
@@ -43,7 +50,7 @@ int in_addr_is_link_local(int family, const union in_addr_union *u) {
assert(u);
if (family == AF_INET)
- return (be32toh(u->in.s_addr) & 0xFFFF0000) == (169U << 24 | 254U << 16);
+ return (be32toh(u->in.s_addr) & UINT32_C(0xFFFF0000)) == (UINT32_C(169) << 24 | UINT32_C(254) << 16);
if (family == AF_INET6)
return IN6_IS_ADDR_LINKLOCAL(&u->in6);
@@ -51,6 +58,19 @@ int in_addr_is_link_local(int family, const union in_addr_union *u) {
return -EAFNOSUPPORT;
}
+int in_addr_is_localhost(int family, const union in_addr_union *u) {
+ assert(u);
+
+ if (family == AF_INET)
+ /* All of 127.x.x.x is localhost. */
+ return (be32toh(u->in.s_addr) & UINT32_C(0xFF000000)) == UINT32_C(127) << 24;
+
+ if (family == AF_INET6)
+ return IN6_IS_ADDR_LOOPBACK(&u->in6);
+
+ return -EAFNOSUPPORT;
+}
+
int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b) {
assert(a);
assert(b);
@@ -199,7 +219,7 @@ int in_addr_to_string(int family, const union in_addr_union *u, char **ret) {
errno = 0;
if (!inet_ntop(family, u, x, l)) {
free(x);
- return errno ? -errno : -EINVAL;
+ return errno > 0 ? -errno : -EINVAL;
}
*ret = x;
@@ -216,7 +236,7 @@ int in_addr_from_string(int family, const char *s, union in_addr_union *ret) {
errno = 0;
if (inet_pton(family, s, ret) <= 0)
- return errno ? -errno : -EINVAL;
+ return errno > 0 ? -errno : -EINVAL;
return 0;
}
diff --git a/src/basic/in-addr-util.h b/src/basic/in-addr-util.h
index 51af08868c..f2b8865df5 100644
--- a/src/basic/in-addr-util.h
+++ b/src/basic/in-addr-util.h
@@ -22,6 +22,8 @@
***/
#include <netinet/in.h>
+#include <stddef.h>
+#include <sys/socket.h>
#include "macro.h"
#include "util.h"
@@ -31,8 +33,14 @@ union in_addr_union {
struct in6_addr in6;
};
+struct in_addr_data {
+ int family;
+ union in_addr_union address;
+};
+
int in_addr_is_null(int family, const union in_addr_union *u);
int in_addr_is_link_local(int family, const union in_addr_union *u);
+int in_addr_is_localhost(int family, const union in_addr_union *u);
int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b);
int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen);
int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen);
diff --git a/src/basic/io-util.c b/src/basic/io-util.c
new file mode 100644
index 0000000000..e83e7cec72
--- /dev/null
+++ b/src/basic/io-util.c
@@ -0,0 +1,266 @@
+/*-*- 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 <errno.h>
+#include <limits.h>
+#include <poll.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "io-util.h"
+#include "time-util.h"
+
+int flush_fd(int fd) {
+ struct pollfd pollfd = {
+ .fd = fd,
+ .events = POLLIN,
+ };
+
+ for (;;) {
+ char buf[LINE_MAX];
+ ssize_t l;
+ int r;
+
+ r = poll(&pollfd, 1, 0);
+ if (r < 0) {
+ if (errno == EINTR)
+ continue;
+
+ return -errno;
+
+ } else if (r == 0)
+ return 0;
+
+ l = read(fd, buf, sizeof(buf));
+ if (l < 0) {
+
+ if (errno == EINTR)
+ continue;
+
+ if (errno == EAGAIN)
+ return 0;
+
+ return -errno;
+ } else if (l == 0)
+ return 0;
+ }
+}
+
+ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
+ uint8_t *p = buf;
+ ssize_t n = 0;
+
+ assert(fd >= 0);
+ assert(buf);
+
+ /* If called with nbytes == 0, let's call read() at least
+ * once, to validate the operation */
+
+ if (nbytes > (size_t) SSIZE_MAX)
+ return -EINVAL;
+
+ do {
+ ssize_t k;
+
+ k = read(fd, p, nbytes);
+ if (k < 0) {
+ if (errno == EINTR)
+ continue;
+
+ if (errno == EAGAIN && do_poll) {
+
+ /* We knowingly ignore any return value here,
+ * and expect that any error/EOF is reported
+ * via read() */
+
+ (void) fd_wait_for_event(fd, POLLIN, USEC_INFINITY);
+ continue;
+ }
+
+ return n > 0 ? n : -errno;
+ }
+
+ if (k == 0)
+ return n;
+
+ assert((size_t) k <= nbytes);
+
+ p += k;
+ nbytes -= k;
+ n += k;
+ } while (nbytes > 0);
+
+ 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 (int) 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;
+
+ assert(fd >= 0);
+ assert(buf);
+
+ if (nbytes > (size_t) SSIZE_MAX)
+ return -EINVAL;
+
+ do {
+ ssize_t k;
+
+ k = write(fd, p, nbytes);
+ if (k < 0) {
+ if (errno == EINTR)
+ continue;
+
+ if (errno == EAGAIN && do_poll) {
+ /* We knowingly ignore any return value here,
+ * and expect that any error/EOF is reported
+ * via write() */
+
+ (void) fd_wait_for_event(fd, POLLOUT, USEC_INFINITY);
+ continue;
+ }
+
+ return -errno;
+ }
+
+ if (_unlikely_(nbytes > 0 && k == 0)) /* Can't really happen */
+ return -EIO;
+
+ assert((size_t) k <= nbytes);
+
+ p += k;
+ nbytes -= k;
+ } while (nbytes > 0);
+
+ return 0;
+}
+
+int pipe_eof(int fd) {
+ struct pollfd pollfd = {
+ .fd = fd,
+ .events = POLLIN|POLLHUP,
+ };
+
+ int r;
+
+ r = poll(&pollfd, 1, 0);
+ if (r < 0)
+ return -errno;
+
+ if (r == 0)
+ return 0;
+
+ return pollfd.revents & POLLHUP;
+}
+
+int fd_wait_for_event(int fd, int event, usec_t t) {
+
+ struct pollfd pollfd = {
+ .fd = fd,
+ .events = event,
+ };
+
+ struct timespec ts;
+ int r;
+
+ r = ppoll(&pollfd, 1, t == USEC_INFINITY ? NULL : timespec_store(&ts, t), NULL);
+ if (r < 0)
+ return -errno;
+
+ if (r == 0)
+ return 0;
+
+ return pollfd.revents;
+}
+
+static size_t nul_length(const uint8_t *p, size_t sz) {
+ size_t n = 0;
+
+ while (sz > 0) {
+ if (*p != 0)
+ break;
+
+ n++;
+ p++;
+ sz--;
+ }
+
+ return n;
+}
+
+ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length) {
+ const uint8_t *q, *w, *e;
+ ssize_t l;
+
+ q = w = p;
+ e = q + sz;
+ while (q < e) {
+ size_t n;
+
+ n = nul_length(q, e - q);
+
+ /* If there are more than the specified run length of
+ * NUL bytes, or if this is the beginning or the end
+ * of the buffer, then seek instead of write */
+ if ((n > run_length) ||
+ (n > 0 && q == p) ||
+ (n > 0 && q + n >= e)) {
+ if (q > w) {
+ l = write(fd, w, q - w);
+ if (l < 0)
+ return -errno;
+ if (l != q -w)
+ return -EIO;
+ }
+
+ if (lseek(fd, n, SEEK_CUR) == (off_t) -1)
+ return -errno;
+
+ q += n;
+ w = q;
+ } else if (n > 0)
+ q += n;
+ else
+ q ++;
+ }
+
+ if (q > w) {
+ l = write(fd, w, q - w);
+ if (l < 0)
+ return -errno;
+ if (l != q - w)
+ return -EIO;
+ }
+
+ return q - (const uint8_t*) p;
+}
diff --git a/src/basic/io-util.h b/src/basic/io-util.h
new file mode 100644
index 0000000000..5f77a556c0
--- /dev/null
+++ b/src/basic/io-util.h
@@ -0,0 +1,79 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include "macro.h"
+#include "time-util.h"
+
+int flush_fd(int fd);
+
+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);
+
+int pipe_eof(int fd);
+
+int fd_wait_for_event(int fd, int event, usec_t timeout);
+
+ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length);
+
+#define IOVEC_SET_STRING(i, s) \
+ do { \
+ struct iovec *_i = &(i); \
+ char *_s = (char *)(s); \
+ _i->iov_base = _s; \
+ _i->iov_len = strlen(_s); \
+ } while(false)
+
+static inline size_t IOVEC_TOTAL_SIZE(const struct iovec *i, unsigned n) {
+ unsigned j;
+ size_t r = 0;
+
+ for (j = 0; j < n; j++)
+ r += i[j].iov_len;
+
+ return r;
+}
+
+static inline size_t IOVEC_INCREMENT(struct iovec *i, unsigned n, size_t k) {
+ unsigned j;
+
+ for (j = 0; j < n; j++) {
+ size_t sub;
+
+ if (_unlikely_(k <= 0))
+ break;
+
+ sub = MIN(i[j].iov_len, k);
+ i[j].iov_len -= sub;
+ i[j].iov_base = (uint8_t*) i[j].iov_base + sub;
+ k -= sub;
+ }
+
+ return k;
+}
diff --git a/src/basic/ioprio.h b/src/basic/ioprio.h
index e5c71d0043..d8bb6eb497 100644
--- a/src/basic/ioprio.h
+++ b/src/basic/ioprio.h
@@ -4,8 +4,8 @@
/* This is minimal version of Linux' linux/ioprio.h header file, which
* is licensed GPL2 */
-#include <unistd.h>
#include <sys/syscall.h>
+#include <unistd.h>
/*
* Gives us 8 prio classes with 13-bits of data for each class
diff --git a/src/basic/json.c b/src/basic/json.c
index be40a0d203..1523e9fb09 100644
--- a/src/basic/json.c
+++ b/src/basic/json.c
@@ -19,11 +19,18 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
+#include <errno.h>
#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "alloc-util.h"
+#include "hexdecoct.h"
+#include "json.h"
#include "macro.h"
+#include "string-util.h"
#include "utf8.h"
-#include "json.h"
int json_variant_new(JsonVariant **ret, JsonVariantType type) {
JsonVariant *v;
diff --git a/src/basic/json.h b/src/basic/json.h
index e0b4d810b5..df3f62f206 100644
--- a/src/basic/json.h
+++ b/src/basic/json.h
@@ -22,6 +22,10 @@
***/
#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "macro.h"
#include "util.h"
enum {
diff --git a/src/basic/label.c b/src/basic/label.c
index 82f10b21bd..f72a985967 100644
--- a/src/basic/label.c
+++ b/src/basic/label.c
@@ -19,10 +19,14 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "label.h"
+#include "macro.h"
#include "selinux-util.h"
#include "smack-util.h"
-#include "util.h"
-#include "label.h"
int label_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
int r, q;
diff --git a/src/basic/locale-util.c b/src/basic/locale-util.c
index 61db9a8125..7784d02168 100644
--- a/src/basic/locale-util.c
+++ b/src/basic/locale-util.c
@@ -19,14 +19,29 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <langinfo.h>
+#include <libintl.h>
+#include <locale.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
#include <sys/mman.h>
+#include <sys/stat.h>
+#include "dirent-util.h"
+#include "fd-util.h"
+#include "hashmap.h"
+#include "locale-util.h"
+#include "path-util.h"
#include "set.h"
-#include "util.h"
-#include "utf8.h"
+#include "string-table.h"
+#include "string-util.h"
#include "strv.h"
-
-#include "locale-util.h"
+#include "utf8.h"
static int add_locales_from_archive(Set *locales) {
/* Stolen from glibc... */
@@ -204,6 +219,88 @@ bool locale_is_valid(const char *name) {
return true;
}
+void init_gettext(void) {
+ setlocale(LC_ALL, "");
+ textdomain(GETTEXT_PACKAGE);
+}
+
+bool is_locale_utf8(void) {
+ const char *set;
+ static int cached_answer = -1;
+
+ /* Note that we default to 'true' here, since today UTF8 is
+ * pretty much supported everywhere. */
+
+ if (cached_answer >= 0)
+ goto out;
+
+ if (!setlocale(LC_ALL, "")) {
+ cached_answer = true;
+ goto out;
+ }
+
+ set = nl_langinfo(CODESET);
+ if (!set) {
+ cached_answer = true;
+ goto out;
+ }
+
+ if (streq(set, "UTF-8")) {
+ cached_answer = true;
+ goto out;
+ }
+
+ /* For LC_CTYPE=="C" return true, because CTYPE is effectly
+ * unset and everything can do to UTF-8 nowadays. */
+ set = setlocale(LC_CTYPE, NULL);
+ if (!set) {
+ cached_answer = true;
+ goto out;
+ }
+
+ /* Check result, but ignore the result if C was set
+ * explicitly. */
+ cached_answer =
+ STR_IN_SET(set, "C", "POSIX") &&
+ !getenv("LC_ALL") &&
+ !getenv("LC_CTYPE") &&
+ !getenv("LANG");
+
+out:
+ return (bool) cached_answer;
+}
+
+
+const char *draw_special_char(DrawSpecialChar ch) {
+
+ static const char *draw_table[2][_DRAW_SPECIAL_CHAR_MAX] = {
+
+ /* UTF-8 */ {
+ [DRAW_TREE_VERTICAL] = "\342\224\202 ", /* │ */
+ [DRAW_TREE_BRANCH] = "\342\224\234\342\224\200", /* ├─ */
+ [DRAW_TREE_RIGHT] = "\342\224\224\342\224\200", /* └─ */
+ [DRAW_TREE_SPACE] = " ", /* */
+ [DRAW_TRIANGULAR_BULLET] = "\342\200\243", /* ‣ */
+ [DRAW_BLACK_CIRCLE] = "\342\227\217", /* ● */
+ [DRAW_ARROW] = "\342\206\222", /* → */
+ [DRAW_DASH] = "\342\200\223", /* – */
+ },
+
+ /* ASCII fallback */ {
+ [DRAW_TREE_VERTICAL] = "| ",
+ [DRAW_TREE_BRANCH] = "|-",
+ [DRAW_TREE_RIGHT] = "`-",
+ [DRAW_TREE_SPACE] = " ",
+ [DRAW_TRIANGULAR_BULLET] = ">",
+ [DRAW_BLACK_CIRCLE] = "*",
+ [DRAW_ARROW] = "->",
+ [DRAW_DASH] = "-",
+ }
+ };
+
+ return draw_table[!is_locale_utf8()][ch];
+}
+
static const char * const locale_variable_table[_VARIABLE_LC_MAX] = {
[VARIABLE_LANG] = "LANG",
[VARIABLE_LANGUAGE] = "LANGUAGE",
diff --git a/src/basic/locale-util.h b/src/basic/locale-util.h
index e48aa3d9af..c71d145139 100644
--- a/src/basic/locale-util.h
+++ b/src/basic/locale-util.h
@@ -21,6 +21,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <libintl.h>
#include <stdbool.h>
#include "macro.h"
@@ -50,5 +51,25 @@ typedef enum LocaleVariable {
int get_locales(char ***l);
bool locale_is_valid(const char *name);
+#define _(String) gettext(String)
+#define N_(String) String
+void init_gettext(void);
+
+bool is_locale_utf8(void);
+
+typedef enum DrawSpecialChar {
+ DRAW_TREE_VERTICAL,
+ DRAW_TREE_BRANCH,
+ DRAW_TREE_RIGHT,
+ DRAW_TREE_SPACE,
+ DRAW_TRIANGULAR_BULLET,
+ DRAW_BLACK_CIRCLE,
+ DRAW_ARROW,
+ DRAW_DASH,
+ _DRAW_SPECIAL_CHAR_MAX
+} DrawSpecialChar;
+
+const char *draw_special_char(DrawSpecialChar ch);
+
const char* locale_variable_to_string(LocaleVariable i) _const_;
LocaleVariable locale_variable_from_string(const char *s) _pure_;
diff --git a/src/basic/lockfile-util.c b/src/basic/lockfile-util.c
index f3ec6a3e52..6ecfc2ec46 100644
--- a/src/basic/lockfile-util.c
+++ b/src/basic/lockfile-util.c
@@ -19,17 +19,19 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
-#include <stdbool.h>
#include <errno.h>
-#include <string.h>
+#include <fcntl.h>
#include <stdio.h>
-#include <limits.h>
+#include <string.h>
#include <sys/file.h>
+#include <sys/stat.h>
-#include "util.h"
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fs-util.h"
#include "lockfile-util.h"
-#include "fileio.h"
+#include "macro.h"
+#include "path-util.h"
int make_lock_file(const char *p, int operation, LockFile *ret) {
_cleanup_close_ int fd = -1;
diff --git a/src/basic/lockfile-util.h b/src/basic/lockfile-util.h
index 38d47094bd..3c514c9e62 100644
--- a/src/basic/lockfile-util.h
+++ b/src/basic/lockfile-util.h
@@ -21,6 +21,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <stddef.h>
+
#include "macro.h"
#include "missing.h"
diff --git a/src/basic/log.c b/src/basic/log.c
index 38f42b3a6e..a2bc0d5be2 100644
--- a/src/basic/log.c
+++ b/src/basic/log.c
@@ -19,26 +19,43 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdarg.h>
-#include <stdio.h>
#include <errno.h>
-#include <unistd.h>
#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/signalfd.h>
#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/uio.h>
#include <sys/un.h>
-#include <stddef.h>
-#include <printf.h>
+#include <time.h>
+#include <unistd.h>
#include "sd-messages.h"
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "formats-util.h"
+#include "io-util.h"
#include "log.h"
-#include "util.h"
-#include "missing.h"
#include "macro.h"
-#include "socket-util.h"
-#include "formats-util.h"
+#include "missing.h"
+#include "parse-util.h"
+#include "proc-cmdline.h"
#include "process-util.h"
-#include "terminal-util.h"
#include "signal-util.h"
+#include "socket-util.h"
+#include "stdio-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "syslog-util.h"
+#include "terminal-util.h"
+#include "time-util.h"
+#include "util.h"
#define SNDBUF_SIZE (8*1024*1024)
@@ -335,7 +352,7 @@ static int write_to_console(
highlight = LOG_PRI(level) <= LOG_ERR && show_color;
if (show_location) {
- snprintf(location, sizeof(location), "(%s:%i) ", file, line);
+ xsprintf(location, "(%s:%i) ", file, line);
IOVEC_SET_STRING(iovec[n++], location);
}
@@ -435,7 +452,7 @@ static int write_to_syslog(
static int write_to_kmsg(
int level,
int error,
- const char*file,
+ const char *file,
int line,
const char *func,
const char *object_field,
@@ -506,7 +523,7 @@ static int log_do_header(
static int write_to_journal(
int level,
int error,
- const char*file,
+ const char *file,
int line,
const char *func,
const char *object_field,
@@ -640,7 +657,7 @@ int log_dump_internal(
int log_internalv(
int level,
int error,
- const char*file,
+ const char *file,
int line,
const char *func,
const char *format,
@@ -667,7 +684,7 @@ int log_internalv(
int log_internal(
int level,
int error,
- const char*file,
+ const char *file,
int line,
const char *func,
const char *format, ...) {
@@ -685,7 +702,7 @@ int log_internal(
int log_object_internalv(
int level,
int error,
- const char*file,
+ const char *file,
int line,
const char *func,
const char *object_field,
@@ -729,7 +746,7 @@ int log_object_internalv(
int log_object_internal(
int level,
int error,
- const char*file,
+ const char *file,
int line,
const char *func,
const char *object_field,
@@ -760,7 +777,7 @@ static void log_assert(
return;
DISABLE_WARNING_FORMAT_NONLITERAL;
- snprintf(buffer, sizeof(buffer), format, text, file, line, func);
+ xsprintf(buffer, format, text, file, line, func);
REENABLE_WARNING;
log_abort_msg = buffer;
@@ -922,7 +939,7 @@ int log_set_max_level_from_string(const char *e) {
t = log_level_from_string(e);
if (t < 0)
- return t;
+ return -EINVAL;
log_set_max_level(t);
return 0;
diff --git a/src/basic/log.h b/src/basic/log.h
index 569762d083..cda1e45cc8 100644
--- a/src/basic/log.h
+++ b/src/basic/log.h
@@ -21,14 +21,15 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdbool.h>
+#include <errno.h>
#include <stdarg.h>
+#include <stdbool.h>
#include <stdlib.h>
-#include <syslog.h>
#include <sys/signalfd.h>
-#include <errno.h>
+#include <syslog.h>
#include "sd-id128.h"
+
#include "macro.h"
typedef enum LogTarget{
@@ -227,3 +228,15 @@ int log_syntax_internal(
? log_syntax_internal(unit, _level, config_file, config_line, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \
: -abs(_e); \
})
+
+#define log_syntax_invalid_utf8(unit, level, config_file, config_line, rvalue) \
+ ({ \
+ int _level = (level); \
+ if (log_get_max_level() >= LOG_PRI(_level)) { \
+ _cleanup_free_ char *_p = NULL; \
+ _p = utf8_escape_invalid(rvalue); \
+ log_syntax_internal(unit, _level, config_file, config_line, 0, __FILE__, __LINE__, __func__, \
+ "String is not UTF-8 clean, ignoring assignment: %s", strna(_p)); \
+ } \
+ -EINVAL; \
+ })
diff --git a/src/basic/login-util.c b/src/basic/login-util.c
index e25437f0f4..4e08fe3895 100644
--- a/src/basic/login-util.c
+++ b/src/basic/login-util.c
@@ -19,8 +19,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <string.h>
+
#include "login-util.h"
-#include "def.h"
+#include "string-util.h"
bool session_id_valid(const char *id) {
diff --git a/src/basic/login-util.h b/src/basic/login-util.h
index a79f20c1b1..be5bb64870 100644
--- a/src/basic/login-util.h
+++ b/src/basic/login-util.h
@@ -22,5 +22,10 @@
#pragma once
#include <stdbool.h>
+#include <unistd.h>
bool session_id_valid(const char *id);
+
+static inline bool logind_running(void) {
+ return access("/run/systemd/seats/", F_OK) >= 0;
+}
diff --git a/src/basic/macro.h b/src/basic/macro.h
index f55d65e2f1..c529c6ecad 100644
--- a/src/basic/macro.h
+++ b/src/basic/macro.h
@@ -22,11 +22,10 @@
***/
#include <assert.h>
-#include <sys/param.h>
-#include <sys/types.h>
-#include <sys/uio.h>
#include <inttypes.h>
#include <stdbool.h>
+#include <sys/param.h>
+#include <sys/types.h>
#define _printf_(a,b) __attribute__ ((format (printf, a, b)))
#define _alloc_(...) __attribute__ ((alloc_size(__VA_ARGS__)))
@@ -295,111 +294,10 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
#define PTR_TO_SIZE(p) ((size_t) ((uintptr_t) (p)))
#define SIZE_TO_PTR(u) ((void *) ((uintptr_t) (u)))
-/* The following macros add 1 when converting things, since UID 0 is a
- * valid UID, while the pointer NULL is special */
-#define PTR_TO_UID(p) ((uid_t) (((uintptr_t) (p))-1))
-#define UID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1))
-
-#define PTR_TO_GID(p) ((gid_t) (((uintptr_t) (p))-1))
-#define GID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1))
-
-#define PTR_TO_PID(p) ((pid_t) ((uintptr_t) p))
-#define PID_TO_PTR(p) ((void*) ((uintptr_t) p))
-
-#define memzero(x,l) (memset((x), 0, (l)))
-#define zero(x) (memzero(&(x), sizeof(x)))
-
#define CHAR_TO_STR(x) ((char[2]) { x, 0 })
#define char_array_0(x) x[sizeof(x)-1] = 0;
-#define IOVEC_SET_STRING(i, s) \
- do { \
- struct iovec *_i = &(i); \
- char *_s = (char *)(s); \
- _i->iov_base = _s; \
- _i->iov_len = strlen(_s); \
- } while(false)
-
-static inline size_t IOVEC_TOTAL_SIZE(const struct iovec *i, unsigned n) {
- unsigned j;
- size_t r = 0;
-
- for (j = 0; j < n; j++)
- r += i[j].iov_len;
-
- return r;
-}
-
-static inline size_t IOVEC_INCREMENT(struct iovec *i, unsigned n, size_t k) {
- unsigned j;
-
- for (j = 0; j < n; j++) {
- size_t sub;
-
- if (_unlikely_(k <= 0))
- break;
-
- sub = MIN(i[j].iov_len, k);
- i[j].iov_len -= sub;
- i[j].iov_base = (uint8_t*) i[j].iov_base + sub;
- k -= sub;
- }
-
- return k;
-}
-
-#define VA_FORMAT_ADVANCE(format, ap) \
-do { \
- int _argtypes[128]; \
- size_t _i, _k; \
- _k = parse_printf_format((format), ELEMENTSOF(_argtypes), _argtypes); \
- assert(_k < ELEMENTSOF(_argtypes)); \
- for (_i = 0; _i < _k; _i++) { \
- if (_argtypes[_i] & PA_FLAG_PTR) { \
- (void) va_arg(ap, void*); \
- continue; \
- } \
- \
- switch (_argtypes[_i]) { \
- case PA_INT: \
- case PA_INT|PA_FLAG_SHORT: \
- case PA_CHAR: \
- (void) va_arg(ap, int); \
- break; \
- case PA_INT|PA_FLAG_LONG: \
- (void) va_arg(ap, long int); \
- break; \
- case PA_INT|PA_FLAG_LONG_LONG: \
- (void) va_arg(ap, long long int); \
- break; \
- case PA_WCHAR: \
- (void) va_arg(ap, wchar_t); \
- break; \
- case PA_WSTRING: \
- case PA_STRING: \
- case PA_POINTER: \
- (void) va_arg(ap, void*); \
- break; \
- case PA_FLOAT: \
- case PA_DOUBLE: \
- (void) va_arg(ap, double); \
- break; \
- case PA_DOUBLE|PA_FLAG_LONG_DOUBLE: \
- (void) va_arg(ap, long double); \
- break; \
- default: \
- assert_not_reached("Unknown format string argument."); \
- } \
- } \
-} while(false)
-
- /* Because statfs.t_type can be int on some architectures, we have to cast
- * the const magic to the type, otherwise the compiler warns about
- * signed/unsigned comparison, because the magic can be 32 bit unsigned.
- */
-#define F_TYPE_EQUAL(a, b) (a == (typeof(a)) b)
-
/* Returns the number of chars needed to format variables of the
* specified type as a decimal string. Adds in extra space for a
* negative '-' prefix (hence works correctly on signed
@@ -410,38 +308,61 @@ do { \
sizeof(type) <= 4 ? 10 : \
sizeof(type) <= 8 ? 20 : sizeof(int[-2*(sizeof(type) > 8)])))
+#define DECIMAL_STR_WIDTH(x) \
+ ({ \
+ typeof(x) _x_ = (x); \
+ unsigned ans = 1; \
+ while (_x_ /= 10) \
+ ans++; \
+ ans; \
+ })
+
#define SET_FLAG(v, flag, b) \
(v) = (b) ? ((v) | (flag)) : ((v) & ~(flag))
-#define IN_SET(x, y, ...) \
- ({ \
- static const typeof(y) _array[] = { (y), __VA_ARGS__ }; \
- const typeof(y) _x = (x); \
- unsigned _i; \
- bool _found = false; \
- for (_i = 0; _i < ELEMENTSOF(_array); _i++) \
- if (_array[_i] == _x) { \
- _found = true; \
- break; \
- } \
- _found; \
+#define CASE_F(X) case X:
+#define CASE_F_1(CASE, X) CASE_F(X)
+#define CASE_F_2(CASE, X, ...) CASE(X) CASE_F_1(CASE, __VA_ARGS__)
+#define CASE_F_3(CASE, X, ...) CASE(X) CASE_F_2(CASE, __VA_ARGS__)
+#define CASE_F_4(CASE, X, ...) CASE(X) CASE_F_3(CASE, __VA_ARGS__)
+#define CASE_F_5(CASE, X, ...) CASE(X) CASE_F_4(CASE, __VA_ARGS__)
+#define CASE_F_6(CASE, X, ...) CASE(X) CASE_F_5(CASE, __VA_ARGS__)
+#define CASE_F_7(CASE, X, ...) CASE(X) CASE_F_6(CASE, __VA_ARGS__)
+#define CASE_F_8(CASE, X, ...) CASE(X) CASE_F_7(CASE, __VA_ARGS__)
+#define CASE_F_9(CASE, X, ...) CASE(X) CASE_F_8(CASE, __VA_ARGS__)
+#define CASE_F_10(CASE, X, ...) CASE(X) CASE_F_9(CASE, __VA_ARGS__)
+#define CASE_F_11(CASE, X, ...) CASE(X) CASE_F_10(CASE, __VA_ARGS__)
+#define CASE_F_12(CASE, X, ...) CASE(X) CASE_F_11(CASE, __VA_ARGS__)
+#define CASE_F_13(CASE, X, ...) CASE(X) CASE_F_12(CASE, __VA_ARGS__)
+#define CASE_F_14(CASE, X, ...) CASE(X) CASE_F_13(CASE, __VA_ARGS__)
+#define CASE_F_15(CASE, X, ...) CASE(X) CASE_F_14(CASE, __VA_ARGS__)
+#define CASE_F_16(CASE, X, ...) CASE(X) CASE_F_15(CASE, __VA_ARGS__)
+#define CASE_F_17(CASE, X, ...) CASE(X) CASE_F_16(CASE, __VA_ARGS__)
+#define CASE_F_18(CASE, X, ...) CASE(X) CASE_F_17(CASE, __VA_ARGS__)
+#define CASE_F_19(CASE, X, ...) CASE(X) CASE_F_18(CASE, __VA_ARGS__)
+#define CASE_F_20(CASE, X, ...) CASE(X) CASE_F_19(CASE, __VA_ARGS__)
+
+#define GET_CASE_F(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,NAME,...) NAME
+#define FOR_EACH_MAKE_CASE(...) \
+ GET_CASE_F(__VA_ARGS__,CASE_F_20,CASE_F_19,CASE_F_18,CASE_F_17,CASE_F_16,CASE_F_15,CASE_F_14,CASE_F_13,CASE_F_12,CASE_F_11, \
+ CASE_F_10,CASE_F_9,CASE_F_8,CASE_F_7,CASE_F_6,CASE_F_5,CASE_F_4,CASE_F_3,CASE_F_2,CASE_F_1) \
+ (CASE_F,__VA_ARGS__)
+
+#define IN_SET(x, ...) \
+ ({ \
+ bool _found = false; \
+ /* If the build breaks in the line below, you need to extend the case macros */ \
+ static _unused_ char _static_assert__macros_need_to_be_extended[20 - sizeof((int[]){__VA_ARGS__})/sizeof(int)]; \
+ switch(x) { \
+ FOR_EACH_MAKE_CASE(__VA_ARGS__) \
+ _found = true; \
+ break; \
+ default: \
+ break; \
+ } \
+ _found; \
})
-/* Return a nulstr for a standard cascade of configuration directories,
- * suitable to pass to conf_files_list_nulstr or config_parse_many. */
-#define CONF_DIRS_NULSTR(n) \
- "/etc/" n ".d\0" \
- "/run/" n ".d\0" \
- "/usr/local/lib/" n ".d\0" \
- "/usr/lib/" n ".d\0" \
- CONF_DIR_SPLIT_USR(n)
-
-#ifdef HAVE_SPLIT_USR
-#define CONF_DIR_SPLIT_USR(n) "/lib/" n ".d\0"
-#else
-#define CONF_DIR_SPLIT_USR(n)
-#endif
-
/* Define C11 thread_local attribute even on older gcc compiler
* version */
#ifndef thread_local
@@ -466,10 +387,6 @@ do { \
#endif
#endif
-#define UID_INVALID ((uid_t) -1)
-#define GID_INVALID ((gid_t) -1)
-#define MODE_INVALID ((mode_t) -1)
-
#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \
static inline void func##p(type *p) { \
if (*p) \
@@ -477,7 +394,4 @@ do { \
} \
struct __useless_struct_to_allow_trailing_semicolon__
-#define CMSG_FOREACH(cmsg, mh) \
- for ((cmsg) = CMSG_FIRSTHDR(mh); (cmsg); (cmsg) = CMSG_NXTHDR((mh), (cmsg)))
-
#include "log.h"
diff --git a/src/basic/memfd-util.c b/src/basic/memfd-util.c
index e99a738e1f..789638f013 100644
--- a/src/basic/memfd-util.c
+++ b/src/basic/memfd-util.c
@@ -19,19 +19,24 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
+#include <errno.h>
#include <fcntl.h>
-#include <sys/mman.h>
-#include <sys/prctl.h>
-
+#include <sys/stat.h>
+#include <unistd.h>
#ifdef HAVE_LINUX_MEMFD_H
-# include <linux/memfd.h>
+#include <linux/memfd.h>
#endif
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
-#include "util.h"
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "macro.h"
#include "memfd-util.h"
-#include "utf8.h"
#include "missing.h"
+#include "string-util.h"
+#include "utf8.h"
int memfd_new(const char *name) {
_cleanup_free_ char *g = NULL;
diff --git a/src/basic/memfd-util.h b/src/basic/memfd-util.h
index 3ed551fb37..2a89361c4c 100644
--- a/src/basic/memfd-util.h
+++ b/src/basic/memfd-util.h
@@ -21,7 +21,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-
+#include <inttypes.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
int memfd_new(const char *name);
int memfd_new_and_map(const char *name, size_t sz, void **p);
diff --git a/src/basic/mempool.c b/src/basic/mempool.c
index d5d98d8829..1822d3956f 100644
--- a/src/basic/mempool.c
+++ b/src/basic/mempool.c
@@ -20,8 +20,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "mempool.h"
+#include <stdint.h>
+#include <stdlib.h>
+
#include "macro.h"
+#include "mempool.h"
#include "util.h"
struct pool {
diff --git a/src/basic/missing.h b/src/basic/missing.h
index 9811b6b23e..c187afa287 100644
--- a/src/basic/missing.h
+++ b/src/basic/missing.h
@@ -23,19 +23,20 @@
/* Missing glibc definitions to access certain kernel APIs */
-#include <sys/resource.h>
-#include <sys/syscall.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <unistd.h>
#include <errno.h>
-#include <linux/oom.h>
-#include <linux/input.h>
-#include <linux/if_link.h>
-#include <linux/loop.h>
+#include <fcntl.h>
#include <linux/audit.h>
#include <linux/capability.h>
+#include <linux/if_link.h>
+#include <linux/input.h>
+#include <linux/loop.h>
#include <linux/neighbour.h>
+#include <linux/oom.h>
+#include <linux/rtnetlink.h>
+#include <stdlib.h>
+#include <sys/resource.h>
+#include <sys/syscall.h>
+#include <unistd.h>
#ifdef HAVE_AUDIT
#include <libaudit.h>
@@ -126,6 +127,14 @@
#define SOL_NETLINK 270
#endif
+#ifndef NETLINK_LIST_MEMBERSHIPS
+#define NETLINK_LIST_MEMBERSHIPS 9
+#endif
+
+#ifndef SOL_SCTP
+#define SOL_SCTP 132
+#endif
+
#if !HAVE_DECL_PIVOT_ROOT
static inline int pivot_root(const char *new_root, const char *put_old) {
return syscall(SYS_pivot_root, new_root, put_old);
@@ -248,6 +257,10 @@ static inline int getrandom(void *buffer, size_t count, unsigned flags) {
#define BTRFS_SEARCH_ARGS_BUFSIZE (4096 - sizeof(struct btrfs_ioctl_search_key))
#endif
+#ifndef BTRFS_QGROUP_LEVEL_SHIFT
+#define BTRFS_QGROUP_LEVEL_SHIFT 48
+#endif
+
#ifndef HAVE_LINUX_BTRFS_H
struct btrfs_ioctl_vol_args {
int64_t fd;
@@ -486,6 +499,10 @@ struct btrfs_ioctl_quota_ctl_args {
#define BTRFS_QGROUP_LIMIT_KEY 244
#endif
+#ifndef BTRFS_QGROUP_RELATION_KEY
+#define BTRFS_QGROUP_RELATION_KEY 246
+#endif
+
#ifndef BTRFS_ROOT_BACKREF_KEY
#define BTRFS_ROOT_BACKREF_KEY 144
#endif
@@ -842,6 +859,19 @@ static inline int setns(int fd, int nstype) {
#define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1)
#endif
+#if !HAVE_DECL_IFLA_BR_PRIORITY
+#define IFLA_BR_UNSPEC 0
+#define IFLA_BR_FORWARD_DELAY 1
+#define IFLA_BR_HELLO_TIME 2
+#define IFLA_BR_MAX_AGE 3
+#define IFLA_BR_AGEING_TIME 4
+#define IFLA_BR_STP_STATE 5
+#define IFLA_BR_PRIORITY 6
+#define __IFLA_BR_MAX 7
+
+#define IFLA_BR_MAX (__IFLA_BR_MAX - 1)
+#endif
+
#if !HAVE_DECL_IFLA_BRPORT_LEARNING_SYNC
#define IFLA_BRPORT_UNSPEC 0
#define IFLA_BRPORT_STATE 1
@@ -875,6 +905,10 @@ static inline int setns(int fd, int nstype) {
#define NDA_MAX (__NDA_MAX - 1)
#endif
+#ifndef RTA_PREF
+#define RTA_PREF 20
+#endif
+
#ifndef IPV6_UNICAST_IF
#define IPV6_UNICAST_IF 76
#endif
@@ -940,6 +974,10 @@ static inline int setns(int fd, int nstype) {
#define IFA_FLAGS 8
#endif
+#ifndef IFA_F_MANAGETEMPADDR
+#define IFA_F_MANAGETEMPADDR 0x100
+#endif
+
#ifndef IFA_F_NOPREFIXROUTE
#define IFA_F_NOPREFIXROUTE 0x200
#endif
@@ -1050,3 +1088,64 @@ static inline int kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, uns
#ifndef INPUT_PROP_ACCELEROMETER
#define INPUT_PROP_ACCELEROMETER 0x06
#endif
+
+#if !HAVE_DECL_KEY_SERIAL_T
+typedef int32_t key_serial_t;
+#endif
+
+#if !HAVE_DECL_KEYCTL
+static inline long keyctl(int cmd, unsigned long arg2, unsigned long arg3, unsigned long arg4,unsigned long arg5) {
+#if defined(__NR_keyctl)
+ return syscall(__NR_keyctl, cmd, arg2, arg3, arg4, arg5);
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+static inline key_serial_t add_key(const char *type, const char *description, const void *payload, size_t plen, key_serial_t ringid) {
+#if defined (__NR_add_key)
+ return syscall(__NR_add_key, type, description, payload, plen, ringid);
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+static inline key_serial_t request_key(const char *type, const char *description, const char * callout_info, key_serial_t destringid) {
+#if defined (__NR_request_key)
+ return syscall(__NR_request_key, type, description, callout_info, destringid);
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+#endif
+
+#ifndef KEYCTL_READ
+#define KEYCTL_READ 11
+#endif
+
+#ifndef KEYCTL_SET_TIMEOUT
+#define KEYCTL_SET_TIMEOUT 15
+#endif
+
+#ifndef KEY_SPEC_USER_KEYRING
+#define KEY_SPEC_USER_KEYRING -4
+#endif
+
+#ifndef PR_CAP_AMBIENT
+#define PR_CAP_AMBIENT 47
+#endif
+
+#ifndef PR_CAP_AMBIENT_IS_SET
+#define PR_CAP_AMBIENT_IS_SET 1
+#endif
+
+#ifndef PR_CAP_AMBIENT_RAISE
+#define PR_CAP_AMBIENT_RAISE 2
+#endif
+
+#ifndef PR_CAP_AMBIENT_CLEAR_ALL
+#define PR_CAP_AMBIENT_CLEAR_ALL 4
+#endif
diff --git a/src/basic/mkdir-label.c b/src/basic/mkdir-label.c
index 76bbc1edda..c9e17f7680 100644
--- a/src/basic/mkdir-label.c
+++ b/src/basic/mkdir-label.c
@@ -20,8 +20,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
#include "label.h"
#include "mkdir.h"
diff --git a/src/basic/mkdir.c b/src/basic/mkdir.c
index 7ee4546988..9f9d52b5df 100644
--- a/src/basic/mkdir.c
+++ b/src/basic/mkdir.c
@@ -19,12 +19,17 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <string.h>
#include <errno.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/stat.h>
-#include "util.h"
-#include "path-util.h"
+#include "fs-util.h"
+#include "macro.h"
#include "mkdir.h"
+#include "path-util.h"
+#include "stat-util.h"
+#include "user-util.h"
int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, mkdir_func_t _mkdir) {
struct stat st;
diff --git a/src/basic/mount-util.c b/src/basic/mount-util.c
new file mode 100644
index 0000000000..10a6536cfc
--- /dev/null
+++ b/src/basic/mount-util.c
@@ -0,0 +1,533 @@
+/*-*- 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 <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <unistd.h>
+
+#include "alloc-util.h"
+#include "escape.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "hashmap.h"
+#include "mount-util.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "set.h"
+#include "stdio-util.h"
+#include "string-util.h"
+
+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;
+ }
+
+ 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;
+ bool nosupp = false, check_st_dev = true;
+ struct stat a, b;
+ int r;
+
+ assert(fd >= 0);
+ assert(filename);
+
+ /* 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
+ * 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 simpler logic. */
+ goto fallback_fdinfo;
+ else if (errno == EOPNOTSUPP)
+ /* This kernel or file system does not support
+ * 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
+ return -errno;
+ }
+
+ 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_fdinfo;
+ else
+ /* 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
+ return -errno;
+ }
+
+ /* 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 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;
+
+ 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 *canonical = NULL, *parent = NULL;
+
+ assert(t);
+
+ if (path_equal(t, "/"))
+ return 1;
+
+ /* we need to resolve symlinks manually, we can't just rely on
+ * fd_is_mount_point() to do that for us; if we have a structure like
+ * /bin -> /usr/bin/ and /usr is a mount point, then the parent that we
+ * look at needs to be /usr, not /. */
+ if (flags & AT_SYMLINK_FOLLOW) {
+ canonical = canonicalize_file_name(t);
+ if (!canonical)
+ return -errno;
+
+ t = canonical;
+ }
+
+ parent = dirname_malloc(t);
+ if (!parent)
+ return -ENOMEM;
+
+ fd = openat(AT_FDCWD, parent, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_PATH);
+ if (fd < 0)
+ return -errno;
+
+ return fd_is_mount_point(fd, basename(t), flags);
+}
+
+int umount_recursive(const char *prefix, int flags) {
+ bool again;
+ int n = 0, r;
+
+ /* Try to umount everything recursively below a
+ * directory. Also, take care of stacked mounts, and keep
+ * unmounting them until they are gone. */
+
+ do {
+ _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
+
+ again = false;
+ r = 0;
+
+ proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
+ if (!proc_self_mountinfo)
+ return -errno;
+
+ for (;;) {
+ _cleanup_free_ char *path = NULL, *p = NULL;
+ int k;
+
+ k = fscanf(proc_self_mountinfo,
+ "%*s " /* (1) mount id */
+ "%*s " /* (2) parent id */
+ "%*s " /* (3) major:minor */
+ "%*s " /* (4) root */
+ "%ms " /* (5) mount point */
+ "%*s" /* (6) mount options */
+ "%*[^-]" /* (7) optional fields */
+ "- " /* (8) separator */
+ "%*s " /* (9) file system type */
+ "%*s" /* (10) mount source */
+ "%*s" /* (11) mount options 2 */
+ "%*[^\n]", /* some rubbish at the end */
+ &path);
+ if (k != 1) {
+ if (k == EOF)
+ break;
+
+ continue;
+ }
+
+ r = cunescape(path, UNESCAPE_RELAX, &p);
+ if (r < 0)
+ return r;
+
+ if (!path_startswith(p, prefix))
+ continue;
+
+ if (umount2(p, flags) < 0) {
+ r = -errno;
+ continue;
+ }
+
+ again = true;
+ n++;
+
+ break;
+ }
+
+ } while (again);
+
+ return r ? r : n;
+}
+
+static int get_mount_flags(const char *path, unsigned long *flags) {
+ struct statvfs buf;
+
+ if (statvfs(path, &buf) < 0)
+ return -errno;
+ *flags = buf.f_flag;
+ return 0;
+}
+
+int bind_remount_recursive(const char *prefix, bool ro) {
+ _cleanup_set_free_free_ Set *done = NULL;
+ _cleanup_free_ char *cleaned = NULL;
+ int r;
+
+ /* Recursively remount a directory (and all its submounts)
+ * read-only or read-write. If the directory is already
+ * mounted, we reuse the mount and simply mark it
+ * MS_BIND|MS_RDONLY (or remove the MS_RDONLY for read-write
+ * operation). If it isn't we first make it one. Afterwards we
+ * apply MS_BIND|MS_RDONLY (or remove MS_RDONLY) to all
+ * submounts we can access, too. When mounts are stacked on
+ * the same mount point we only care for each individual
+ * "top-level" mount on each point, as we cannot
+ * influence/access the underlying mounts anyway. We do not
+ * have any effect on future submounts that might get
+ * propagated, they migt be writable. This includes future
+ * submounts that have been triggered via autofs. */
+
+ cleaned = strdup(prefix);
+ if (!cleaned)
+ return -ENOMEM;
+
+ path_kill_slashes(cleaned);
+
+ done = set_new(&string_hash_ops);
+ if (!done)
+ return -ENOMEM;
+
+ for (;;) {
+ _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
+ _cleanup_set_free_free_ Set *todo = NULL;
+ bool top_autofs = false;
+ char *x;
+ unsigned long orig_flags;
+
+ todo = set_new(&string_hash_ops);
+ if (!todo)
+ return -ENOMEM;
+
+ proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
+ if (!proc_self_mountinfo)
+ return -errno;
+
+ for (;;) {
+ _cleanup_free_ char *path = NULL, *p = NULL, *type = NULL;
+ int k;
+
+ k = fscanf(proc_self_mountinfo,
+ "%*s " /* (1) mount id */
+ "%*s " /* (2) parent id */
+ "%*s " /* (3) major:minor */
+ "%*s " /* (4) root */
+ "%ms " /* (5) mount point */
+ "%*s" /* (6) mount options (superblock) */
+ "%*[^-]" /* (7) optional fields */
+ "- " /* (8) separator */
+ "%ms " /* (9) file system type */
+ "%*s" /* (10) mount source */
+ "%*s" /* (11) mount options (bind mount) */
+ "%*[^\n]", /* some rubbish at the end */
+ &path,
+ &type);
+ if (k != 2) {
+ if (k == EOF)
+ break;
+
+ continue;
+ }
+
+ 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
+ * them, as we don't make any guarantees for
+ * future submounts anyway. If they are
+ * already triggered, then we will find
+ * another entry for this. */
+ if (streq(type, "autofs")) {
+ top_autofs = top_autofs || path_equal(cleaned, p);
+ continue;
+ }
+
+ if (path_startswith(p, cleaned) &&
+ !set_contains(done, p)) {
+
+ r = set_consume(todo, p);
+ p = NULL;
+
+ if (r == -EEXIST)
+ continue;
+ if (r < 0)
+ return r;
+ }
+ }
+
+ /* If we have no submounts to process anymore and if
+ * the root is either already done, or an autofs, we
+ * are done */
+ if (set_isempty(todo) &&
+ (top_autofs || set_contains(done, cleaned)))
+ return 0;
+
+ if (!set_contains(done, cleaned) &&
+ !set_contains(todo, cleaned)) {
+ /* The prefix directory itself is not yet a
+ * mount, make it one. */
+ if (mount(cleaned, cleaned, NULL, MS_BIND|MS_REC, NULL) < 0)
+ return -errno;
+
+ orig_flags = 0;
+ (void) get_mount_flags(cleaned, &orig_flags);
+ orig_flags &= ~MS_RDONLY;
+
+ if (mount(NULL, prefix, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0)
+ return -errno;
+
+ x = strdup(cleaned);
+ if (!x)
+ return -ENOMEM;
+
+ r = set_consume(done, x);
+ if (r < 0)
+ return r;
+ }
+
+ while ((x = set_steal_first(todo))) {
+
+ r = set_consume(done, x);
+ if (r == -EEXIST || r == 0)
+ continue;
+ if (r < 0)
+ return r;
+
+ /* Try to reuse the original flag set, but
+ * don't care for errors, in case of
+ * obstructed mounts */
+ orig_flags = 0;
+ (void) get_mount_flags(x, &orig_flags);
+ orig_flags &= ~MS_RDONLY;
+
+ if (mount(NULL, x, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0) {
+
+ /* Deal with mount points that are
+ * obstructed by a later mount */
+
+ if (errno != ENOENT)
+ return -errno;
+ }
+
+ }
+ }
+}
+
+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;
+}
+
+bool fstype_is_network(const char *fstype) {
+ static const char table[] =
+ "afs\0"
+ "cifs\0"
+ "smbfs\0"
+ "sshfs\0"
+ "ncpfs\0"
+ "ncp\0"
+ "nfs\0"
+ "nfs4\0"
+ "gfs\0"
+ "gfs2\0"
+ "glusterfs\0";
+
+ const char *x;
+
+ x = startswith(fstype, "fuse.");
+ if (x)
+ fstype = x;
+
+ return nulstr_contains(table, fstype);
+}
+
+int repeat_unmount(const char *path, int flags) {
+ bool done = false;
+
+ assert(path);
+
+ /* If there are multiple mounts on a mount point, this
+ * removes them all */
+
+ for (;;) {
+ if (umount2(path, flags) < 0) {
+
+ if (errno == EINVAL)
+ return done;
+
+ return -errno;
+ }
+
+ done = true;
+ }
+}
diff --git a/src/basic/mount-util.h b/src/basic/mount-util.h
new file mode 100644
index 0000000000..b37250f08e
--- /dev/null
+++ b/src/basic/mount-util.h
@@ -0,0 +1,54 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <fcntl.h>
+#include <mntent.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "macro.h"
+#include "missing.h"
+
+int fd_is_mount_point(int fd, const char *filename, int flags);
+int path_is_mount_point(const char *path, int flags);
+
+int repeat_unmount(const char *path, int flags);
+
+int umount_recursive(const char *target, int flags);
+int bind_remount_recursive(const char *prefix, bool ro);
+
+int mount_move_root(const char *path);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, endmntent);
+#define _cleanup_endmntent_ _cleanup_(endmntentp)
+
+bool fstype_is_network(const char *fstype);
+
+union file_handle_union {
+ struct file_handle handle;
+ char padding[sizeof(struct file_handle) + MAX_HANDLE_SZ];
+};
+
+#define FILE_HANDLE_INIT { .handle.handle_bytes = MAX_HANDLE_SZ }
diff --git a/src/shared/nss-util.h b/src/basic/nss-util.h
index 3657aa5d9c..cc30d93aad 100644
--- a/src/shared/nss-util.h
+++ b/src/basic/nss-util.h
@@ -21,12 +21,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <nss.h>
+#include <grp.h>
#include <netdb.h>
-#include <resolv.h>
+#include <nss.h>
#include <pwd.h>
-#include <grp.h>
-
+#include <resolv.h>
#define NSS_GETHOSTBYNAME_PROTOTYPES(module) \
enum nss_status _nss_##module##_gethostbyname4_r( \
diff --git a/src/basic/ordered-set.h b/src/basic/ordered-set.h
index 6c617ab305..da10e90ff2 100644
--- a/src/basic/ordered-set.h
+++ b/src/basic/ordered-set.h
@@ -29,6 +29,17 @@ static inline OrderedSet* ordered_set_new(const struct hash_ops *ops) {
return (OrderedSet*) ordered_hashmap_new(ops);
}
+static inline int ordered_set_ensure_allocated(OrderedSet **s, const struct hash_ops *ops) {
+ if (*s)
+ return 0;
+
+ *s = ordered_set_new(ops);
+ if (!*s)
+ return -ENOMEM;
+
+ return 0;
+}
+
static inline OrderedSet* ordered_set_free(OrderedSet *s) {
ordered_hashmap_free((OrderedHashmap*) s);
return NULL;
diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c
new file mode 100644
index 0000000000..d8de6f90ea
--- /dev/null
+++ b/src/basic/parse-util.c
@@ -0,0 +1,536 @@
+/*-*- 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 <errno.h>
+#include <inttypes.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <xlocale.h>
+
+#include "alloc-util.h"
+#include "extract-word.h"
+#include "macro.h"
+#include "parse-util.h"
+#include "string-util.h"
+
+int parse_boolean(const char *v) {
+ assert(v);
+
+ if (streq(v, "1") || strcaseeq(v, "yes") || strcaseeq(v, "y") || strcaseeq(v, "true") || strcaseeq(v, "t") || strcaseeq(v, "on"))
+ return 1;
+ else if (streq(v, "0") || strcaseeq(v, "no") || strcaseeq(v, "n") || strcaseeq(v, "false") || strcaseeq(v, "f") || strcaseeq(v, "off"))
+ return 0;
+
+ return -EINVAL;
+}
+
+int parse_pid(const char *s, pid_t* ret_pid) {
+ unsigned long ul = 0;
+ pid_t pid;
+ int r;
+
+ assert(s);
+ assert(ret_pid);
+
+ r = safe_atolu(s, &ul);
+ if (r < 0)
+ return r;
+
+ pid = (pid_t) ul;
+
+ if ((unsigned long) pid != ul)
+ return -ERANGE;
+
+ if (pid <= 0)
+ return -ERANGE;
+
+ *ret_pid = pid;
+ return 0;
+}
+
+int parse_mode(const char *s, mode_t *ret) {
+ char *x;
+ long l;
+
+ assert(s);
+ assert(ret);
+
+ s += strspn(s, WHITESPACE);
+ if (s[0] == '-')
+ return -ERANGE;
+
+ 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 parse_ifindex(const char *s, int *ret) {
+ int ifi, r;
+
+ r = safe_atoi(s, &ifi);
+ if (r < 0)
+ return r;
+ if (ifi <= 0)
+ return -EINVAL;
+
+ *ret = ifi;
+ return 0;
+}
+
+int parse_size(const char *t, uint64_t base, uint64_t *size) {
+
+ /* 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
+ * 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!
+ *
+ * In either case we use just K, M, G as suffix, and not Ki,
+ * Mi, Gi or so (as IEC would suggest). That's because that's
+ * frickin' ugly. But this means you really need to make sure
+ * to document which base you are parsing when you use this
+ * call. */
+
+ struct table {
+ const char *suffix;
+ unsigned long long factor;
+ };
+
+ static const struct table iec[] = {
+ { "E", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
+ { "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
+ { "T", 1024ULL*1024ULL*1024ULL*1024ULL },
+ { "G", 1024ULL*1024ULL*1024ULL },
+ { "M", 1024ULL*1024ULL },
+ { "K", 1024ULL },
+ { "B", 1ULL },
+ { "", 1ULL },
+ };
+
+ static const struct table si[] = {
+ { "E", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL*1000ULL },
+ { "P", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL },
+ { "T", 1000ULL*1000ULL*1000ULL*1000ULL },
+ { "G", 1000ULL*1000ULL*1000ULL },
+ { "M", 1000ULL*1000ULL },
+ { "K", 1000ULL },
+ { "B", 1ULL },
+ { "", 1ULL },
+ };
+
+ const struct table *table;
+ const char *p;
+ unsigned long long r = 0;
+ unsigned n_entries, start_pos = 0;
+
+ assert(t);
+ assert(base == 1000 || base == 1024);
+ assert(size);
+
+ if (base == 1000) {
+ table = si;
+ n_entries = ELEMENTSOF(si);
+ } else {
+ table = iec;
+ n_entries = ELEMENTSOF(iec);
+ }
+
+ p = t;
+ do {
+ unsigned long long l, tmp;
+ double frac = 0;
+ char *e;
+ unsigned i;
+
+ p += strspn(p, WHITESPACE);
+
+ errno = 0;
+ l = strtoull(p, &e, 10);
+ if (errno > 0)
+ return -errno;
+ if (e == p)
+ return -EINVAL;
+ if (*p == '-')
+ return -ERANGE;
+
+ if (*e == '.') {
+ e++;
+
+ /* strtoull() itself would accept space/+/- */
+ if (*e >= '0' && *e <= '9') {
+ unsigned long long l2;
+ char *e2;
+
+ l2 = strtoull(e, &e2, 10);
+ if (errno > 0)
+ return -errno;
+
+ /* Ignore failure. E.g. 10.M is valid */
+ frac = l2;
+ for (; e < e2; e++)
+ frac /= 10;
+ }
+ }
+
+ e += strspn(e, WHITESPACE);
+
+ for (i = start_pos; i < n_entries; i++)
+ if (startswith(e, table[i].suffix))
+ break;
+
+ if (i >= n_entries)
+ return -EINVAL;
+
+ if (l + (frac > 0) > ULLONG_MAX / table[i].factor)
+ return -ERANGE;
+
+ tmp = l * table[i].factor + (unsigned long long) (frac * table[i].factor);
+ if (tmp > ULLONG_MAX - r)
+ return -ERANGE;
+
+ r += tmp;
+ if ((unsigned long long) (uint64_t) r != r)
+ return -ERANGE;
+
+ p = e + strlen(table[i].suffix);
+
+ start_pos = i + 1;
+
+ } while (*p);
+
+ *size = r;
+
+ return 0;
+}
+
+int parse_range(const char *t, unsigned *lower, unsigned *upper) {
+ _cleanup_free_ char *word = NULL;
+ unsigned l, u;
+ int r;
+
+ assert(lower);
+ assert(upper);
+
+ /* Extract the lower bound. */
+ r = extract_first_word(&t, &word, "-", EXTRACT_DONT_COALESCE_SEPARATORS);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EINVAL;
+
+ r = safe_atou(word, &l);
+ if (r < 0)
+ return r;
+
+ /* Check for the upper bound and extract it if needed */
+ if (!t)
+ /* Single number with no dashes. */
+ u = l;
+ else if (!*t)
+ /* Trailing dash is an error. */
+ return -EINVAL;
+ else {
+ r = safe_atou(t, &u);
+ if (r < 0)
+ return r;
+ }
+
+ *lower = l;
+ *upper = u;
+ return 0;
+}
+
+char *format_bytes(char *buf, size_t l, uint64_t t) {
+ unsigned i;
+
+ /* This only does IEC units so far */
+
+ static const struct {
+ const char *suffix;
+ uint64_t factor;
+ } table[] = {
+ { "E", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
+ { "P", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
+ { "T", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
+ { "G", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
+ { "M", UINT64_C(1024)*UINT64_C(1024) },
+ { "K", UINT64_C(1024) },
+ };
+
+ if (t == (uint64_t) -1)
+ return NULL;
+
+ for (i = 0; i < ELEMENTSOF(table); i++) {
+
+ if (t >= table[i].factor) {
+ snprintf(buf, l,
+ "%" PRIu64 ".%" PRIu64 "%s",
+ t / table[i].factor,
+ ((t*UINT64_C(10)) / table[i].factor) % UINT64_C(10),
+ table[i].suffix);
+
+ goto finish;
+ }
+ }
+
+ snprintf(buf, l, "%" PRIu64 "B", t);
+
+finish:
+ buf[l-1] = 0;
+ return buf;
+
+}
+
+int safe_atou(const char *s, unsigned *ret_u) {
+ char *x = NULL;
+ unsigned long l;
+
+ assert(s);
+ assert(ret_u);
+
+ /* strtoul() is happy to parse negative values, and silently
+ * converts them to unsigned values without generating an
+ * error. We want a clean error, hence let's look for the "-"
+ * prefix on our own, and generate an error. But let's do so
+ * only after strtoul() validated that the string is clean
+ * otherwise, so that we return EINVAL preferably over
+ * ERANGE. */
+
+ s += strspn(s, WHITESPACE);
+
+ errno = 0;
+ l = strtoul(s, &x, 0);
+ if (errno > 0)
+ return -errno;
+ if (!x || x == s || *x)
+ return -EINVAL;
+ if (s[0] == '-')
+ return -ERANGE;
+ if ((unsigned long) (unsigned) l != l)
+ return -ERANGE;
+
+ *ret_u = (unsigned) l;
+ return 0;
+}
+
+int safe_atoi(const char *s, int *ret_i) {
+ char *x = NULL;
+ long l;
+
+ assert(s);
+ assert(ret_i);
+
+ errno = 0;
+ l = strtol(s, &x, 0);
+ if (errno > 0)
+ return -errno;
+ if (!x || x == s || *x)
+ return -EINVAL;
+ if ((long) (int) l != l)
+ return -ERANGE;
+
+ *ret_i = (int) l;
+ return 0;
+}
+
+int safe_atollu(const char *s, long long unsigned *ret_llu) {
+ char *x = NULL;
+ unsigned long long l;
+
+ assert(s);
+ assert(ret_llu);
+
+ s += strspn(s, WHITESPACE);
+
+ errno = 0;
+ l = strtoull(s, &x, 0);
+ if (errno > 0)
+ return -errno;
+ if (!x || x == s || *x)
+ return -EINVAL;
+ if (*s == '-')
+ return -ERANGE;
+
+ *ret_llu = l;
+ return 0;
+}
+
+int safe_atolli(const char *s, long long int *ret_lli) {
+ char *x = NULL;
+ long long l;
+
+ assert(s);
+ assert(ret_lli);
+
+ errno = 0;
+ l = strtoll(s, &x, 0);
+ if (errno > 0)
+ return -errno;
+ if (!x || x == s || *x)
+ return -EINVAL;
+
+ *ret_lli = l;
+ return 0;
+}
+
+int safe_atou8(const char *s, uint8_t *ret) {
+ char *x = NULL;
+ unsigned long l;
+
+ assert(s);
+ assert(ret);
+
+ s += strspn(s, WHITESPACE);
+
+ errno = 0;
+ l = strtoul(s, &x, 0);
+ if (errno > 0)
+ return -errno;
+ if (!x || x == s || *x)
+ return -EINVAL;
+ if (s[0] == '-')
+ return -ERANGE;
+ if ((unsigned long) (uint8_t) l != l)
+ return -ERANGE;
+
+ *ret = (uint8_t) l;
+ return 0;
+}
+
+int safe_atou16(const char *s, uint16_t *ret) {
+ char *x = NULL;
+ unsigned long l;
+
+ assert(s);
+ assert(ret);
+
+ s += strspn(s, WHITESPACE);
+
+ errno = 0;
+ l = strtoul(s, &x, 0);
+ if (errno > 0)
+ return -errno;
+ if (!x || x == s || *x)
+ return -EINVAL;
+ if (s[0] == '-')
+ return -ERANGE;
+ if ((unsigned long) (uint16_t) l != l)
+ return -ERANGE;
+
+ *ret = (uint16_t) l;
+ return 0;
+}
+
+int safe_atoi16(const char *s, int16_t *ret) {
+ char *x = NULL;
+ long l;
+
+ assert(s);
+ assert(ret);
+
+ errno = 0;
+ l = strtol(s, &x, 0);
+ if (errno > 0)
+ return -errno;
+ if (!x || x == s || *x)
+ return -EINVAL;
+ if ((long) (int16_t) l != l)
+ return -ERANGE;
+
+ *ret = (int16_t) l;
+ return 0;
+}
+
+int safe_atod(const char *s, double *ret_d) {
+ char *x = NULL;
+ double d = 0;
+ locale_t loc;
+
+ assert(s);
+ assert(ret_d);
+
+ loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
+ if (loc == (locale_t) 0)
+ return -errno;
+
+ errno = 0;
+ d = strtod_l(s, &x, loc);
+ if (errno > 0) {
+ freelocale(loc);
+ return -errno;
+ }
+ if (!x || x == s || *x) {
+ freelocale(loc);
+ return -EINVAL;
+ }
+
+ freelocale(loc);
+ *ret_d = (double) d;
+ return 0;
+}
+
+int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) {
+ size_t i;
+ unsigned val = 0;
+ const char *s;
+
+ s = *p;
+
+ /* accept any number of digits, strtoull is limted to 19 */
+ for(i=0; i < digits; i++,s++) {
+ if (*s < '0' || *s > '9') {
+ if (i == 0)
+ return -EINVAL;
+
+ /* too few digits, pad with 0 */
+ for (; i < digits; i++)
+ val *= 10;
+
+ break;
+ }
+
+ val *= 10;
+ val += *s - '0';
+ }
+
+ /* maybe round up */
+ if (*s >= '5' && *s <= '9')
+ val++;
+
+ s += strspn(s, DIGITS);
+
+ *p = s;
+ *res = val;
+
+ return 0;
+}
diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h
new file mode 100644
index 0000000000..af439cfaa7
--- /dev/null
+++ b/src/basic/parse-util.h
@@ -0,0 +1,97 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "macro.h"
+
+#define MODE_INVALID ((mode_t) -1)
+
+int parse_boolean(const char *v) _pure_;
+int parse_pid(const char *s, pid_t* ret_pid);
+int parse_mode(const char *s, mode_t *ret);
+int parse_ifindex(const char *s, int *ret);
+
+int parse_size(const char *t, uint64_t base, uint64_t *size);
+int parse_range(const char *t, unsigned *lower, unsigned *upper);
+
+#define FORMAT_BYTES_MAX 8
+char *format_bytes(char *buf, size_t l, uint64_t t);
+
+int safe_atou(const char *s, unsigned *ret_u);
+int safe_atoi(const char *s, int *ret_i);
+int safe_atollu(const char *s, unsigned long long *ret_u);
+int safe_atolli(const char *s, long long int *ret_i);
+
+int safe_atou8(const char *s, uint8_t *ret);
+
+int safe_atou16(const char *s, uint16_t *ret);
+int safe_atoi16(const char *s, int16_t *ret);
+
+static inline int safe_atou32(const char *s, uint32_t *ret_u) {
+ assert_cc(sizeof(uint32_t) == sizeof(unsigned));
+ return safe_atou(s, (unsigned*) ret_u);
+}
+
+static inline int safe_atoi32(const char *s, int32_t *ret_i) {
+ assert_cc(sizeof(int32_t) == sizeof(int));
+ return safe_atoi(s, (int*) ret_i);
+}
+
+static inline int safe_atou64(const char *s, uint64_t *ret_u) {
+ assert_cc(sizeof(uint64_t) == sizeof(unsigned long long));
+ return safe_atollu(s, (unsigned long long*) ret_u);
+}
+
+static inline int safe_atoi64(const char *s, int64_t *ret_i) {
+ assert_cc(sizeof(int64_t) == sizeof(long long int));
+ return safe_atolli(s, (long long int*) ret_i);
+}
+
+#if LONG_MAX == INT_MAX
+static inline int safe_atolu(const char *s, unsigned long *ret_u) {
+ assert_cc(sizeof(unsigned long) == sizeof(unsigned));
+ return safe_atou(s, (unsigned*) ret_u);
+}
+static inline int safe_atoli(const char *s, long int *ret_u) {
+ assert_cc(sizeof(long int) == sizeof(int));
+ return safe_atoi(s, (int*) ret_u);
+}
+#else
+static inline int safe_atolu(const char *s, unsigned long *ret_u) {
+ assert_cc(sizeof(unsigned long) == sizeof(unsigned long long));
+ return safe_atollu(s, (unsigned long long*) ret_u);
+}
+static inline int safe_atoli(const char *s, long int *ret_u) {
+ assert_cc(sizeof(long int) == sizeof(long long int));
+ return safe_atolli(s, (long long int*) ret_u);
+}
+#endif
+
+int safe_atod(const char *s, double *ret_d);
+
+int parse_fractional_part_u(const char **s, size_t digits, unsigned *res);
diff --git a/src/basic/path-util.c b/src/basic/path-util.c
index 5cbfc145a4..4837bb2d7d 100644
--- a/src/basic/path-util.c
+++ b/src/basic/path-util.c
@@ -19,21 +19,31 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <string.h>
-#include <unistd.h>
#include <errno.h>
-#include <stdlib.h>
+#include <limits.h>
#include <stdio.h>
-#include <fcntl.h>
-#include <sys/statvfs.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
-#include "macro.h"
-#include "util.h"
+/* When we include libgen.h because we need dirname() we immediately
+ * undefine basename() since libgen.h defines it as a macro to the
+ * POSIX version which is really broken. We prefer GNU basename(). */
+#include <libgen.h>
+#undef basename
+
+#include "alloc-util.h"
+#include "extract-word.h"
+#include "fs-util.h"
#include "log.h"
-#include "strv.h"
-#include "path-util.h"
+#include "macro.h"
#include "missing.h"
-#include "fileio.h"
+#include "path-util.h"
+#include "stat-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "time-util.h"
bool path_is_absolute(const char *p) {
return p[0] == '/';
@@ -43,61 +53,25 @@ bool is_path(const char *p) {
return !!strchr(p, '/');
}
-int path_get_parent(const char *path, char **_r) {
- const char *e, *a = NULL, *b = NULL, *p;
- char *r;
- bool slash = false;
-
- assert(path);
- assert(_r);
-
- if (!*path)
- return -EINVAL;
-
- for (e = path; *e; e++) {
-
- if (!slash && *e == '/') {
- a = b;
- b = e;
- slash = true;
- } else if (slash && *e != '/')
- slash = false;
- }
-
- if (*(e-1) == '/')
- p = a;
- else
- p = b;
-
- if (!p)
- return -EINVAL;
-
- if (p == path)
- r = strdup("/");
- else
- r = strndup(path, p-path);
-
- if (!r)
- return -ENOMEM;
-
- *_r = r;
- return 0;
-}
-
-char **path_split_and_make_absolute(const char *p) {
+int path_split_and_make_absolute(const char *p, char ***ret) {
char **l;
+ int r;
+
assert(p);
+ assert(ret);
l = strv_split(p, ":");
if (!l)
- return NULL;
+ return -ENOMEM;
- if (!path_strv_make_absolute_cwd(l)) {
+ r = path_strv_make_absolute_cwd(l);
+ if (r < 0) {
strv_free(l);
- return NULL;
+ return r;
}
- return l;
+ *ret = l;
+ return r;
}
char *path_make_absolute(const char *p, const char *prefix) {
@@ -112,22 +86,31 @@ char *path_make_absolute(const char *p, const char *prefix) {
return strjoin(prefix, "/", p, NULL);
}
-char *path_make_absolute_cwd(const char *p) {
- _cleanup_free_ char *cwd = NULL;
+int path_make_absolute_cwd(const char *p, char **ret) {
+ char *c;
assert(p);
+ assert(ret);
/* Similar to path_make_absolute(), but prefixes with the
* current working directory. */
if (path_is_absolute(p))
- return strdup(p);
+ c = strdup(p);
+ else {
+ _cleanup_free_ char *cwd = NULL;
- cwd = get_current_dir_name();
- if (!cwd)
- return NULL;
+ cwd = get_current_dir_name();
+ if (!cwd)
+ return negative_errno();
+
+ c = strjoin(cwd, "/", p, NULL);
+ }
+ if (!c)
+ return -ENOMEM;
- return strjoin(cwd, "/", p, NULL);
+ *ret = c;
+ return 0;
}
int path_make_relative(const char *from_dir, const char *to_path, char **_r) {
@@ -215,8 +198,9 @@ int path_make_relative(const char *from_dir, const char *to_path, char **_r) {
return 0;
}
-char **path_strv_make_absolute_cwd(char **l) {
+int path_strv_make_absolute_cwd(char **l) {
char **s;
+ int r;
/* Goes through every item in the string list and makes it
* absolute. This works in place and won't rollback any
@@ -225,15 +209,15 @@ char **path_strv_make_absolute_cwd(char **l) {
STRV_FOREACH(s, l) {
char *t;
- t = path_make_absolute_cwd(*s);
- if (!t)
- return NULL;
+ r = path_make_absolute_cwd(*s, &t);
+ if (r < 0)
+ return r;
free(*s);
*s = t;
}
- return l;
+ return 0;
}
char **path_strv_resolve(char **l, const char *prefix) {
@@ -411,7 +395,7 @@ int path_compare(const char *a, const char *b) {
* 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)
+ if (d != 0)
return d;
for (;;) {
@@ -434,12 +418,12 @@ int path_compare(const char *a, const char *b) {
/* Alphabetical sort: "/foo/aaa" before "/foo/b" */
d = memcmp(a, b, MIN(j, k));
- if (d)
+ if (d != 0)
return (d > 0) - (d < 0); /* sign of d */
/* Sort "/foo/a" before "/foo/aaa" */
d = (j > k) - (j < k); /* sign of (j - k) */
- if (d)
+ if (d != 0)
return d;
a += j;
@@ -471,294 +455,66 @@ char* path_join(const char *root, const char *path, const char *rest) {
NULL);
}
-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);
- }
+int find_binary(const char *name, char **ret) {
+ int last_error, r;
+ const char *p;
- 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;
- }
-
- 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;
- bool nosupp = false, check_st_dev = true;
- struct stat a, b;
- int r;
-
- assert(fd >= 0);
- assert(filename);
-
- /* 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
- * 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 simpler logic. */
- goto fallback_fdinfo;
- else if (errno == EOPNOTSUPP)
- /* This kernel or file system does not support
- * 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
- return -errno;
- }
+ assert(name);
- 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_fdinfo;
- else
- /* 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
+ if (is_path(name)) {
+ if (access(name, X_OK) < 0)
return -errno;
- }
-
- /* 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 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;
-
- 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 *canonical = NULL, *parent = NULL;
- int r;
-
- assert(t);
-
- if (path_equal(t, "/"))
- return 1;
-
- /* we need to resolve symlinks manually, we can't just rely on
- * fd_is_mount_point() to do that for us; if we have a structure like
- * /bin -> /usr/bin/ and /usr is a mount point, then the parent that we
- * look at needs to be /usr, not /. */
- if (flags & AT_SYMLINK_FOLLOW) {
- canonical = canonicalize_file_name(t);
- if (!canonical)
- return -errno;
+ if (ret) {
+ r = path_make_absolute_cwd(name, ret);
+ if (r < 0)
+ return r;
+ }
- t = canonical;
+ return 0;
}
- r = path_get_parent(t, &parent);
- if (r < 0)
- return r;
-
- fd = openat(AT_FDCWD, parent, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_PATH);
- if (fd < 0)
- return -errno;
-
- return fd_is_mount_point(fd, basename(t), flags);
-}
-
-int path_is_read_only_fs(const char *path) {
- struct statvfs st;
-
- assert(path);
-
- if (statvfs(path, &st) < 0)
- return -errno;
-
- if (st.f_flag & ST_RDONLY)
- return true;
-
- /* On NFS, statvfs() might not reflect whether we can actually
- * write to the remote share. Let's try again with
- * access(W_OK) which is more reliable, at least sometimes. */
- if (access(path, W_OK) < 0 && errno == EROFS)
- return true;
-
- return false;
-}
-
-int path_is_os_tree(const char *path) {
- char *p;
- int r;
-
- /* We use /usr/lib/os-release as flag file if something is an OS */
- p = strjoina(path, "/usr/lib/os-release");
- r = access(p, F_OK);
-
- if (r >= 0)
- return 1;
-
- /* Also check for the old location in /etc, just in case. */
- p = strjoina(path, "/etc/os-release");
- r = access(p, F_OK);
-
- return r >= 0;
-}
-
-int find_binary(const char *name, bool local, char **filename) {
- assert(name);
+ /**
+ * Plain getenv, not secure_getenv, because we want
+ * to actually allow the user to pick the binary.
+ */
+ p = getenv("PATH");
+ if (!p)
+ p = DEFAULT_PATH;
- if (is_path(name)) {
- if (local && access(name, X_OK) < 0)
- return -errno;
+ last_error = -ENOENT;
- if (filename) {
- char *p;
+ for (;;) {
+ _cleanup_free_ char *j = NULL, *element = NULL;
- p = path_make_absolute_cwd(name);
- if (!p)
- return -ENOMEM;
+ r = extract_first_word(&p, &element, ":", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
- *filename = p;
- }
+ if (!path_is_absolute(element))
+ continue;
- return 0;
- } else {
- const char *path;
- const char *word, *state;
- size_t l;
-
- /**
- * Plain getenv, not secure_getenv, because we want
- * to actually allow the user to pick the binary.
- */
- path = getenv("PATH");
- if (!path)
- path = DEFAULT_PATH;
-
- FOREACH_WORD_SEPARATOR(word, l, path, ":", state) {
- _cleanup_free_ char *p = NULL;
-
- if (asprintf(&p, "%.*s/%s", (int) l, word, name) < 0)
- return -ENOMEM;
+ j = strjoin(element, "/", name, NULL);
+ if (!j)
+ return -ENOMEM;
- if (access(p, X_OK) < 0)
- continue;
+ if (access(j, X_OK) >= 0) {
+ /* Found it! */
- if (filename) {
- *filename = path_kill_slashes(p);
- p = NULL;
+ if (ret) {
+ *ret = path_kill_slashes(j);
+ j = NULL;
}
return 0;
}
- return -ENOENT;
+ last_error = -errno;
}
+
+ return last_error;
}
bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
@@ -796,14 +552,13 @@ bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool upd
return changed;
}
-int fsck_exists(const char *fstype) {
+static int binary_is_good(const char *binary) {
_cleanup_free_ char *p = NULL, *d = NULL;
- const char *checker;
int r;
- checker = strjoina("fsck.", fstype);
-
- r = find_binary(checker, true, &p);
+ r = find_binary(binary, &p);
+ if (r == -ENOENT)
+ return 0;
if (r < 0)
return r;
@@ -811,13 +566,39 @@ int fsck_exists(const char *fstype) {
* fsck */
r = readlink_malloc(p, &d);
- if (r >= 0 &&
- (path_equal(d, "/bin/true") ||
- path_equal(d, "/usr/bin/true") ||
- path_equal(d, "/dev/null")))
- return -ENOENT;
+ if (r == -EINVAL) /* not a symlink */
+ return 1;
+ if (r < 0)
+ return r;
- return 0;
+ return !path_equal(d, "true") &&
+ !path_equal(d, "/bin/true") &&
+ !path_equal(d, "/usr/bin/true") &&
+ !path_equal(d, "/dev/null");
+}
+
+int fsck_exists(const char *fstype) {
+ const char *checker;
+
+ assert(fstype);
+
+ if (streq(fstype, "auto"))
+ return -EINVAL;
+
+ checker = strjoina("fsck.", fstype);
+ return binary_is_good(checker);
+}
+
+int mkfs_exists(const char *fstype) {
+ const char *mkfs;
+
+ assert(fstype);
+
+ if (streq(fstype, "auto"))
+ return -EINVAL;
+
+ mkfs = strjoina("mkfs.", fstype);
+ return binary_is_good(mkfs);
}
char *prefix_root(const char *root, const char *path) {
@@ -853,3 +634,166 @@ char *prefix_root(const char *root, const char *path) {
strcpy(p, path);
return n;
}
+
+int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg) {
+ char *p;
+ int r;
+
+ /*
+ * This function is intended to be used in command line
+ * parsers, to handle paths that are passed in. It makes the
+ * path absolute, and reduces it to NULL if omitted or
+ * root (the latter optionally).
+ *
+ * NOTE THAT THIS WILL FREE THE PREVIOUS ARGUMENT POINTER ON
+ * SUCCESS! Hence, do not pass in uninitialized pointers.
+ */
+
+ if (isempty(path)) {
+ *arg = mfree(*arg);
+ return 0;
+ }
+
+ r = path_make_absolute_cwd(path, &p);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse path \"%s\" and make it absolute: %m", path);
+
+ path_kill_slashes(p);
+ if (suppress_root && path_equal(p, "/"))
+ p = mfree(p);
+
+ free(*arg);
+ *arg = p;
+ return 0;
+}
+
+char* dirname_malloc(const char *path) {
+ char *d, *dir, *dir2;
+
+ assert(path);
+
+ d = strdup(path);
+ if (!d)
+ return NULL;
+
+ dir = dirname(d);
+ assert(dir);
+
+ if (dir == d)
+ return d;
+
+ dir2 = strdup(dir);
+ free(d);
+
+ return dir2;
+}
+
+bool filename_is_valid(const char *p) {
+ const char *e;
+
+ if (isempty(p))
+ return false;
+
+ if (streq(p, "."))
+ return false;
+
+ if (streq(p, ".."))
+ return false;
+
+ e = strchrnul(p, '/');
+ if (*e != 0)
+ return false;
+
+ if (e - p > FILENAME_MAX)
+ return false;
+
+ return true;
+}
+
+bool path_is_safe(const char *p) {
+
+ if (isempty(p))
+ return false;
+
+ if (streq(p, "..") || startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../"))
+ return false;
+
+ if (strlen(p)+1 > PATH_MAX)
+ return false;
+
+ /* The following two checks are not really dangerous, but hey, they still are confusing */
+ if (streq(p, ".") || startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./"))
+ return false;
+
+ if (strstr(p, "//"))
+ return false;
+
+ return true;
+}
+
+char *file_in_same_dir(const char *path, const char *filename) {
+ char *e, *ret;
+ size_t k;
+
+ assert(path);
+ assert(filename);
+
+ /* This removes the last component of path and appends
+ * filename, unless the latter is absolute anyway or the
+ * former isn't */
+
+ if (path_is_absolute(filename))
+ return strdup(filename);
+
+ e = strrchr(path, '/');
+ if (!e)
+ return strdup(filename);
+
+ k = strlen(filename);
+ ret = new(char, (e + 1 - path) + k + 1);
+ if (!ret)
+ return NULL;
+
+ memcpy(mempcpy(ret, path, e + 1 - path), filename, k + 1);
+ return ret;
+}
+
+bool hidden_file_allow_backup(const char *filename) {
+ assert(filename);
+
+ return
+ filename[0] == '.' ||
+ streq(filename, "lost+found") ||
+ streq(filename, "aquota.user") ||
+ streq(filename, "aquota.group") ||
+ endswith(filename, ".rpmnew") ||
+ endswith(filename, ".rpmsave") ||
+ endswith(filename, ".rpmorig") ||
+ endswith(filename, ".dpkg-old") ||
+ endswith(filename, ".dpkg-new") ||
+ endswith(filename, ".dpkg-tmp") ||
+ endswith(filename, ".dpkg-dist") ||
+ endswith(filename, ".dpkg-bak") ||
+ endswith(filename, ".dpkg-backup") ||
+ endswith(filename, ".dpkg-remove") ||
+ endswith(filename, ".swp");
+}
+
+bool hidden_file(const char *filename) {
+ assert(filename);
+
+ if (endswith(filename, "~"))
+ return true;
+
+ return hidden_file_allow_backup(filename);
+}
+
+bool is_device_path(const char *path) {
+
+ /* Returns true on paths that refer to a device, either in
+ * sysfs or in /dev */
+
+ return
+ path_startswith(path, "/dev/") ||
+ path_startswith(path, "/sys/");
+}
diff --git a/src/basic/path-util.h b/src/basic/path-util.h
index 1eac89c51b..84472d38c7 100644
--- a/src/basic/path-util.h
+++ b/src/basic/path-util.h
@@ -21,7 +21,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <alloca.h>
#include <stdbool.h>
+#include <stddef.h>
#include "macro.h"
#include "time-util.h"
@@ -36,11 +38,10 @@
#endif
bool is_path(const char *p) _pure_;
-char** path_split_and_make_absolute(const char *p);
-int path_get_parent(const char *path, char **parent);
+int path_split_and_make_absolute(const char *p, char ***ret);
bool path_is_absolute(const char *p) _pure_;
char* path_make_absolute(const char *p, const char *prefix);
-char* path_make_absolute_cwd(const char *p);
+int path_make_absolute_cwd(const char *p, char **ret);
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_;
@@ -49,20 +50,16 @@ 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);
+int 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 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);
-
-int find_binary(const char *name, bool local, char **filename);
+int find_binary(const char *name, char **filename);
bool paths_check_timestamp(const char* const* paths, usec_t *paths_ts_usec, bool update);
int fsck_exists(const char *fstype);
+int mkfs_exists(const char *fstype);
/* Iterates through the path prefixes of the specified path, going up
* the tree, to root. Also returns "" (and not "/"!) for the root
@@ -100,3 +97,17 @@ char *prefix_root(const char *root, const char *path);
} \
_ret; \
})
+
+int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg);
+
+char* dirname_malloc(const char *path);
+
+bool filename_is_valid(const char *p) _pure_;
+bool path_is_safe(const char *p) _pure_;
+
+char *file_in_same_dir(const char *path, const char *filename);
+
+bool hidden_file_allow_backup(const char *filename);
+bool hidden_file(const char *filename) _pure_;
+
+bool is_device_path(const char *path);
diff --git a/src/basic/prioq.c b/src/basic/prioq.c
index b89888be0e..86c5c0e9b4 100644
--- a/src/basic/prioq.c
+++ b/src/basic/prioq.c
@@ -19,7 +19,21 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
+/*
+ * Priority Queue
+ * The prioq object implements a priority queue. That is, it orders objects by
+ * their priority and allows O(1) access to the object with the highest
+ * priority. Insertion and removal are Θ(log n). Optionally, the caller can
+ * provide a pointer to an index which will be kept up-to-date by the prioq.
+ *
+ * The underlying algorithm used in this implementation is a Heap.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include "alloc-util.h"
+#include "hashmap.h"
#include "prioq.h"
struct prioq_item {
@@ -101,7 +115,7 @@ static unsigned shuffle_up(Prioq *q, unsigned idx) {
k = (idx-1)/2;
- if (q->compare_func(q->items[k].data, q->items[idx].data) < 0)
+ if (q->compare_func(q->items[k].data, q->items[idx].data) <= 0)
break;
swap(q, idx, k);
diff --git a/src/basic/prioq.h b/src/basic/prioq.h
index 1c044b135c..6a2451387c 100644
--- a/src/basic/prioq.h
+++ b/src/basic/prioq.h
@@ -21,7 +21,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <stdbool.h>
+
#include "hashmap.h"
+#include "macro.h"
typedef struct Prioq Prioq;
diff --git a/src/basic/proc-cmdline.c b/src/basic/proc-cmdline.c
new file mode 100644
index 0000000000..4e8eba10ab
--- /dev/null
+++ b/src/basic/proc-cmdline.c
@@ -0,0 +1,178 @@
+/*-*- 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 <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "alloc-util.h"
+#include "extract-word.h"
+#include "fileio.h"
+#include "macro.h"
+#include "parse-util.h"
+#include "proc-cmdline.h"
+#include "process-util.h"
+#include "special.h"
+#include "string-util.h"
+#include "util.h"
+#include "virt.h"
+
+int proc_cmdline(char **ret) {
+ assert(ret);
+
+ if (detect_container() > 0)
+ return get_process_cmdline(1, 0, false, ret);
+ else
+ return read_one_line_file("/proc/cmdline", ret);
+}
+
+int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value)) {
+ _cleanup_free_ char *line = NULL;
+ const char *p;
+ int r;
+
+ assert(parse_item);
+
+ r = proc_cmdline(&line);
+ if (r < 0)
+ return r;
+
+ p = line;
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
+ char *value = NULL;
+
+ r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ /* Filter out arguments that are intended only for the
+ * initrd */
+ if (!in_initrd() && startswith(word, "rd."))
+ continue;
+
+ value = strchr(word, '=');
+ if (value)
+ *(value++) = 0;
+
+ r = parse_item(word, value);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int get_proc_cmdline_key(const char *key, char **value) {
+ _cleanup_free_ char *line = NULL, *ret = NULL;
+ bool found = false;
+ const char *p;
+ int r;
+
+ assert(key);
+
+ r = proc_cmdline(&line);
+ if (r < 0)
+ return r;
+
+ p = line;
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
+ const char *e;
+
+ r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ /* Filter out arguments that are intended only for the
+ * initrd */
+ if (!in_initrd() && startswith(word, "rd."))
+ continue;
+
+ if (value) {
+ e = startswith(word, key);
+ if (!e)
+ continue;
+
+ r = free_and_strdup(&ret, e);
+ if (r < 0)
+ return r;
+
+ found = true;
+ } else {
+ if (streq(word, key))
+ found = true;
+ }
+ }
+
+ if (value) {
+ *value = ret;
+ ret = NULL;
+ }
+
+ return found;
+
+}
+
+int shall_restore_state(void) {
+ _cleanup_free_ char *value = NULL;
+ int r;
+
+ r = get_proc_cmdline_key("systemd.restore_state=", &value);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return true;
+
+ return parse_boolean(value);
+}
+
+static const char * const rlmap[] = {
+ "emergency", SPECIAL_EMERGENCY_TARGET,
+ "-b", SPECIAL_EMERGENCY_TARGET,
+ "rescue", SPECIAL_RESCUE_TARGET,
+ "single", SPECIAL_RESCUE_TARGET,
+ "-s", SPECIAL_RESCUE_TARGET,
+ "s", SPECIAL_RESCUE_TARGET,
+ "S", SPECIAL_RESCUE_TARGET,
+ "1", SPECIAL_RESCUE_TARGET,
+ "2", SPECIAL_MULTI_USER_TARGET,
+ "3", SPECIAL_MULTI_USER_TARGET,
+ "4", SPECIAL_MULTI_USER_TARGET,
+ "5", SPECIAL_GRAPHICAL_TARGET,
+};
+
+const char* runlevel_to_target(const char *word) {
+ size_t i;
+
+ if (!word)
+ return NULL;
+
+ for (i = 0; i < ELEMENTSOF(rlmap); i += 2)
+ if (streq(word, rlmap[i]))
+ return rlmap[i+1];
+
+ return NULL;
+}
diff --git a/src/basic/proc-cmdline.h b/src/basic/proc-cmdline.h
new file mode 100644
index 0000000000..ce6e84995a
--- /dev/null
+++ b/src/basic/proc-cmdline.h
@@ -0,0 +1,29 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General 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 proc_cmdline(char **ret);
+int parse_proc_cmdline(int (*parse_word)(const char *key, const char *value));
+int get_proc_cmdline_key(const char *parameter, char **value);
+
+int shall_restore_state(void);
+const char* runlevel_to_target(const char *rl);
diff --git a/src/basic/process-util.c b/src/basic/process-util.c
index cff2d2a034..4341d0093f 100644
--- a/src/basic/process-util.c
+++ b/src/basic/process-util.c
@@ -17,22 +17,41 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <linux/oom.h>
+#include <sched.h>
+#include <signal.h>
#include <stdbool.h>
-#include <sys/types.h>
-#include <string.h>
#include <stdio.h>
-#include <assert.h>
-#include <errno.h>
-#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/personality.h>
+#include <sys/prctl.h>
+#include <sys/types.h>
#include <sys/wait.h>
-#include <signal.h>
-#include <ctype.h>
+#include <syslog.h>
+#include <unistd.h>
+#ifdef HAVE_VALGRIND_VALGRIND_H
+#include <valgrind/valgrind.h>
+#endif
+#include "alloc-util.h"
+#include "escape.h"
+#include "fd-util.h"
#include "fileio.h"
-#include "util.h"
+#include "fs-util.h"
+#include "ioprio.h"
#include "log.h"
-#include "signal-util.h"
+#include "macro.h"
+#include "missing.h"
#include "process-util.h"
+#include "signal-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "user-util.h"
+#include "util.h"
int get_process_state(pid_t pid) {
const char *p;
@@ -174,6 +193,37 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
return 0;
}
+void rename_process(const char name[8]) {
+ assert(name);
+
+ /* This is a like a poor man's setproctitle(). It changes the
+ * comm field, argv[0], and also the glibc's internally used
+ * name of the process. For the first one a limit of 16 chars
+ * applies, to the second one usually one of 10 (i.e. length
+ * of "/sbin/init"), to the third one one of 7 (i.e. length of
+ * "systemd"). If you pass a longer string it will be
+ * truncated */
+
+ prctl(PR_SET_NAME, name);
+
+ if (program_invocation_name)
+ strncpy(program_invocation_name, name, strlen(program_invocation_name));
+
+ if (saved_argc > 0) {
+ int i;
+
+ if (saved_argv[0])
+ strncpy(saved_argv[0], name, strlen(saved_argv[0]));
+
+ for (i = 1; i < saved_argc; i++) {
+ if (!saved_argv[i])
+ break;
+
+ memzero(saved_argv[i], strlen(saved_argv[i]));
+ }
+ }
+}
+
int is_kernel_thread(pid_t pid) {
const char *p;
size_t count;
@@ -215,7 +265,7 @@ int get_process_capeff(pid_t pid, char **capeff) {
p = procfs_file_alloca(pid, "status");
- r = get_status_field(p, "\nCapEff:", capeff);
+ r = get_proc_field(p, "CapEff", WHITESPACE, capeff);
if (r == -ENOENT)
return -ESRCH;
@@ -364,7 +414,7 @@ int get_process_environ(pid_t pid, char **env) {
return 0;
}
-int get_parent_of_pid(pid_t pid, pid_t *_ppid) {
+int get_process_ppid(pid_t pid, pid_t *_ppid) {
int r;
_cleanup_free_ char *line = NULL;
long unsigned ppid;
@@ -476,6 +526,16 @@ int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_cod
return -EPROTO;
}
+void sigkill_wait(pid_t *pid) {
+ if (!pid)
+ return;
+ if (*pid <= 1)
+ return;
+
+ if (kill(*pid, SIGKILL) > 0)
+ (void) wait_for_terminate(*pid, NULL);
+}
+
int kill_and_sigcont(pid_t pid, int sig) {
int r;
@@ -547,9 +607,12 @@ int getenv_for_pid(pid_t pid, const char *field, char **_value) {
bool pid_is_unwaited(pid_t pid) {
/* Checks whether a PID is still valid at all, including a zombie */
- if (pid <= 0)
+ if (pid < 0)
return false;
+ if (pid <= 1) /* If we or PID 1 would be dead and have been waited for, this code would not be running */
+ return true;
+
if (kill(pid, 0) >= 0)
return true;
@@ -561,12 +624,158 @@ bool pid_is_alive(pid_t pid) {
/* Checks whether a PID is still valid and not a zombie */
- if (pid <= 0)
+ if (pid < 0)
return false;
+ if (pid <= 1) /* If we or PID 1 would be a zombie, this code would not be running */
+ return true;
+
r = get_process_state(pid);
if (r == -ESRCH || r == 'Z')
return false;
return true;
}
+
+bool is_main_thread(void) {
+ static thread_local int cached = 0;
+
+ if (_unlikely_(cached == 0))
+ cached = getpid() == gettid() ? 1 : -1;
+
+ return cached > 0;
+}
+
+noreturn void freeze(void) {
+
+ /* Make sure nobody waits for us on a socket anymore */
+ close_all_fds(NULL, 0);
+
+ sync();
+
+ for (;;)
+ pause();
+}
+
+bool oom_score_adjust_is_valid(int oa) {
+ return oa >= OOM_SCORE_ADJ_MIN && oa <= OOM_SCORE_ADJ_MAX;
+}
+
+unsigned long personality_from_string(const char *p) {
+
+ /* Parse a personality specifier. We introduce our own
+ * identifiers that indicate specific ABIs, rather than just
+ * hints regarding the register size, since we want to keep
+ * things open for multiple locally supported ABIs for the
+ * same register size. We try to reuse the ABI identifiers
+ * used by libseccomp. */
+
+#if defined(__x86_64__)
+
+ if (streq(p, "x86"))
+ return PER_LINUX32;
+
+ if (streq(p, "x86-64"))
+ return PER_LINUX;
+
+#elif defined(__i386__)
+
+ if (streq(p, "x86"))
+ return PER_LINUX;
+
+#elif defined(__s390x__)
+
+ if (streq(p, "s390"))
+ return PER_LINUX32;
+
+ if (streq(p, "s390x"))
+ return PER_LINUX;
+
+#elif defined(__s390__)
+
+ if (streq(p, "s390"))
+ return PER_LINUX;
+#endif
+
+ return PERSONALITY_INVALID;
+}
+
+const char* personality_to_string(unsigned long p) {
+
+#if defined(__x86_64__)
+
+ if (p == PER_LINUX32)
+ return "x86";
+
+ if (p == PER_LINUX)
+ return "x86-64";
+
+#elif defined(__i386__)
+
+ if (p == PER_LINUX)
+ return "x86";
+
+#elif defined(__s390x__)
+
+ if (p == PER_LINUX)
+ return "s390x";
+
+ if (p == PER_LINUX32)
+ return "s390";
+
+#elif defined(__s390__)
+
+ if (p == PER_LINUX)
+ return "s390";
+
+#endif
+
+ return NULL;
+}
+
+void valgrind_summary_hack(void) {
+#ifdef HAVE_VALGRIND_VALGRIND_H
+ if (getpid() == 1 && RUNNING_ON_VALGRIND) {
+ pid_t pid;
+ pid = raw_clone(SIGCHLD, NULL);
+ if (pid < 0)
+ log_emergency_errno(errno, "Failed to fork off valgrind helper: %m");
+ else if (pid == 0)
+ exit(EXIT_SUCCESS);
+ else {
+ log_info("Spawned valgrind helper as PID "PID_FMT".", pid);
+ (void) wait_for_terminate(pid, NULL);
+ }
+ }
+#endif
+}
+
+static const char *const ioprio_class_table[] = {
+ [IOPRIO_CLASS_NONE] = "none",
+ [IOPRIO_CLASS_RT] = "realtime",
+ [IOPRIO_CLASS_BE] = "best-effort",
+ [IOPRIO_CLASS_IDLE] = "idle"
+};
+
+DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ioprio_class, int, INT_MAX);
+
+static const char *const sigchld_code_table[] = {
+ [CLD_EXITED] = "exited",
+ [CLD_KILLED] = "killed",
+ [CLD_DUMPED] = "dumped",
+ [CLD_TRAPPED] = "trapped",
+ [CLD_STOPPED] = "stopped",
+ [CLD_CONTINUED] = "continued",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(sigchld_code, int);
+
+static const char* const sched_policy_table[] = {
+ [SCHED_OTHER] = "other",
+ [SCHED_BATCH] = "batch",
+ [SCHED_IDLE] = "idle",
+ [SCHED_FIFO] = "fifo",
+ [SCHED_RR] = "rr"
+};
+
+DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(sched_policy, int, INT_MAX);
diff --git a/src/basic/process-util.h b/src/basic/process-util.h
index 07431d043b..ac4d05e65f 100644
--- a/src/basic/process-util.h
+++ b/src/basic/process-util.h
@@ -19,14 +19,16 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdbool.h>
-#include <sys/types.h>
#include <alloca.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stddef.h>
#include <stdio.h>
#include <string.h>
-#include <signal.h>
+#include <sys/types.h>
#include "formats-util.h"
+#include "macro.h"
#define procfs_file_alloca(pid, field) \
({ \
@@ -51,15 +53,50 @@ 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 get_process_ppid(pid_t pid, pid_t *ppid);
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);
+void sigkill_wait(pid_t *pid);
+#define _cleanup_sigkill_wait_ _cleanup_(sigkill_wait)
+
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);
+
+bool is_main_thread(void);
+
+noreturn void freeze(void);
+
+bool oom_score_adjust_is_valid(int oa);
+
+#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);
+
+int ioprio_class_to_string_alloc(int i, char **s);
+int ioprio_class_from_string(const char *s);
+
+const char *sigchld_code_to_string(int i) _const_;
+int sigchld_code_from_string(const char *s) _pure_;
+
+int sched_policy_to_string_alloc(int i, char **s);
+int sched_policy_from_string(const char *s);
+
+#define PTR_TO_PID(p) ((pid_t) ((uintptr_t) p))
+#define PID_TO_PTR(p) ((void*) ((uintptr_t) p))
+
+void valgrind_summary_hack(void);
diff --git a/src/basic/random-util.c b/src/basic/random-util.c
index b230044f50..e1543da5a3 100644
--- a/src/basic/random-util.c
+++ b/src/basic/random-util.c
@@ -17,21 +17,24 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdint.h>
+#include <elf.h>
#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
#include <fcntl.h>
-#include <time.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <linux/random.h>
+#include <stdint.h>
+
#ifdef HAVE_SYS_AUXV_H
#include <sys/auxv.h>
#endif
-#include <linux/random.h>
+#include "fd-util.h"
+#include "io-util.h"
+#include "missing.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;
diff --git a/src/basic/random-util.h b/src/basic/random-util.h
index f7862c8c8b..3cee4c5014 100644
--- a/src/basic/random-util.h
+++ b/src/basic/random-util.h
@@ -19,6 +19,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <stddef.h>
#include <stdint.h>
int dev_urandom(void *p, size_t n);
diff --git a/src/basic/ratelimit.c b/src/basic/ratelimit.c
index 81fc9c19ff..b62f3da76b 100644
--- a/src/basic/ratelimit.c
+++ b/src/basic/ratelimit.c
@@ -20,6 +20,9 @@
***/
+#include <sys/time.h>
+
+#include "macro.h"
#include "ratelimit.h"
/* Modelled after Linux' lib/ratelimit.c by Dave Young
diff --git a/src/basic/ratelimit.h b/src/basic/ratelimit.h
index 58efca7df1..98c81f6b9e 100644
--- a/src/basic/ratelimit.h
+++ b/src/basic/ratelimit.h
@@ -21,6 +21,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <stdbool.h>
+
+#include "time-util.h"
#include "util.h"
typedef struct RateLimit {
diff --git a/src/basic/replace-var.c b/src/basic/replace-var.c
index 478fc43a38..8c3279b376 100644
--- a/src/basic/replace-var.c
+++ b/src/basic/replace-var.c
@@ -19,12 +19,15 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
+#include <stddef.h>
+#include <stdlib.h>
#include <string.h>
+#include "alloc-util.h"
#include "macro.h"
-#include "util.h"
#include "replace-var.h"
-#include "def.h"
+#include "string-util.h"
/*
* Generic infrastructure for replacing @FOO@ style variables in
diff --git a/src/basic/rlimit-util.c b/src/basic/rlimit-util.c
new file mode 100644
index 0000000000..44f885db16
--- /dev/null
+++ b/src/basic/rlimit-util.c
@@ -0,0 +1,73 @@
+/*-*- 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 <errno.h>
+#include <sys/resource.h>
+
+#include "macro.h"
+#include "missing.h"
+#include "rlimit-util.h"
+#include "string-table.h"
+
+int setrlimit_closest(int resource, const struct rlimit *rlim) {
+ struct rlimit highest, fixed;
+
+ assert(rlim);
+
+ if (setrlimit(resource, rlim) >= 0)
+ return 0;
+
+ if (errno != EPERM)
+ return -errno;
+
+ /* So we failed to set the desired setrlimit, then let's try
+ * to get as close as we can */
+ assert_se(getrlimit(resource, &highest) == 0);
+
+ fixed.rlim_cur = MIN(rlim->rlim_cur, highest.rlim_max);
+ fixed.rlim_max = MIN(rlim->rlim_max, highest.rlim_max);
+
+ if (setrlimit(resource, &fixed) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static const char* const rlimit_table[_RLIMIT_MAX] = {
+ [RLIMIT_CPU] = "LimitCPU",
+ [RLIMIT_FSIZE] = "LimitFSIZE",
+ [RLIMIT_DATA] = "LimitDATA",
+ [RLIMIT_STACK] = "LimitSTACK",
+ [RLIMIT_CORE] = "LimitCORE",
+ [RLIMIT_RSS] = "LimitRSS",
+ [RLIMIT_NOFILE] = "LimitNOFILE",
+ [RLIMIT_AS] = "LimitAS",
+ [RLIMIT_NPROC] = "LimitNPROC",
+ [RLIMIT_MEMLOCK] = "LimitMEMLOCK",
+ [RLIMIT_LOCKS] = "LimitLOCKS",
+ [RLIMIT_SIGPENDING] = "LimitSIGPENDING",
+ [RLIMIT_MSGQUEUE] = "LimitMSGQUEUE",
+ [RLIMIT_NICE] = "LimitNICE",
+ [RLIMIT_RTPRIO] = "LimitRTPRIO",
+ [RLIMIT_RTTIME] = "LimitRTTIME"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(rlimit, int);
diff --git a/src/libsystemd/sd-event/event-util.h b/src/basic/rlimit-util.h
index e7cad9be46..262f86dd04 100644
--- a/src/libsystemd/sd-event/event-util.h
+++ b/src/basic/rlimit-util.h
@@ -5,7 +5,7 @@
/***
This file is part of systemd.
- Copyright 2013 Lennart Poettering
+ 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
@@ -21,11 +21,13 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
-#include "sd-event.h"
+#include <sys/resource.h>
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_event*, sd_event_unref);
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_event_source*, sd_event_source_unref);
+#include "macro.h"
-#define _cleanup_event_unref_ _cleanup_(sd_event_unrefp)
-#define _cleanup_event_source_unref_ _cleanup_(sd_event_source_unrefp)
+const char *rlimit_to_string(int i) _const_;
+int rlimit_from_string(const char *s) _pure_;
+
+int setrlimit_closest(int resource, const struct rlimit *rlim);
+
+#define RLIMIT_MAKE_CONST(lim) ((struct rlimit) { lim, lim })
diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c
index dbbe817684..4807561723 100644
--- a/src/basic/rm-rf.c
+++ b/src/basic/rm-rf.c
@@ -19,10 +19,24 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
-#include "path-util.h"
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <unistd.h>
+
#include "btrfs-util.h"
+#include "fd-util.h"
+#include "log.h"
+#include "macro.h"
+#include "mount-util.h"
+#include "path-util.h"
#include "rm-rf.h"
+#include "stat-util.h"
+#include "string-util.h"
int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
_cleanup_closedir_ DIR *d = NULL;
@@ -68,7 +82,7 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
errno = 0;
de = readdir(d);
if (!de) {
- if (errno != 0 && ret == 0)
+ if (errno > 0 && ret == 0)
ret = -errno;
return ret;
}
@@ -120,7 +134,7 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
/* This could be a subvolume, try to remove it */
- r = btrfs_subvol_remove_fd(fd, de->d_name, true);
+ r = btrfs_subvol_remove_fd(fd, de->d_name, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA);
if (r < 0) {
if (r != -ENOTTY && r != -EINVAL) {
if (ret == 0)
@@ -178,7 +192,7 @@ int rm_rf(const char *path, RemoveFlags flags) {
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);
+ r = btrfs_subvol_remove(path, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA);
if (r >= 0)
return r;
diff --git a/src/basic/selinux-util.c b/src/basic/selinux-util.c
index a39a0f775a..5956c4fe43 100644
--- a/src/basic/selinux-util.c
+++ b/src/basic/selinux-util.c
@@ -21,17 +21,26 @@
#include <errno.h>
#include <malloc.h>
+#include <stddef.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/time.h>
#include <sys/un.h>
+#include <syslog.h>
#ifdef HAVE_SELINUX
-#include <selinux/selinux.h>
-#include <selinux/label.h>
#include <selinux/context.h>
+#include <selinux/label.h>
+#include <selinux/selinux.h>
#endif
-#include "strv.h"
+#include "alloc-util.h"
+#include "log.h"
+#include "macro.h"
#include "path-util.h"
#include "selinux-util.h"
+#include "time-util.h"
+#include "util.h"
#ifdef HAVE_SELINUX
DEFINE_TRIVIAL_CLEANUP_FUNC(security_context_t, freecon);
@@ -46,7 +55,7 @@ static struct selabel_handle *label_hnd = NULL;
#define log_enforcing(...) log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG, __VA_ARGS__)
#endif
-bool mac_selinux_use(void) {
+bool mac_selinux_have(void) {
#ifdef HAVE_SELINUX
if (cached_use < 0)
cached_use = is_selinux_enabled() > 0;
@@ -57,6 +66,16 @@ bool mac_selinux_use(void) {
#endif
}
+bool mac_selinux_use(void) {
+ if (!mac_selinux_have())
+ return false;
+
+ /* Never try to configure SELinux features if we aren't
+ * root */
+
+ return getuid() == 0;
+}
+
void mac_selinux_retest(void) {
#ifdef HAVE_SELINUX
cached_use = -1;
@@ -171,15 +190,15 @@ int mac_selinux_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
int mac_selinux_apply(const char *path, const char *label) {
#ifdef HAVE_SELINUX
- assert(path);
- assert(label);
-
if (!mac_selinux_use())
return 0;
+ assert(path);
+ assert(label);
+
if (setfilecon(path, (security_context_t) label) < 0) {
log_enforcing("Failed to set SELinux security context %s on path %s: %m", label, path);
- if (security_getenforce() == 1)
+ if (security_getenforce() > 0)
return -errno;
}
#endif
@@ -196,7 +215,7 @@ int mac_selinux_get_create_label_from_exe(const char *exe, char **label) {
assert(exe);
assert(label);
- if (!mac_selinux_use())
+ if (!mac_selinux_have())
return -EOPNOTSUPP;
r = getcon_raw(&mycon);
@@ -222,7 +241,7 @@ int mac_selinux_get_our_label(char **label) {
assert(label);
#ifdef HAVE_SELINUX
- if (!mac_selinux_use())
+ if (!mac_selinux_have())
return -EOPNOTSUPP;
r = getcon_raw(label);
@@ -246,7 +265,7 @@ int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *
assert(exe);
assert(label);
- if (!mac_selinux_use())
+ if (!mac_selinux_have())
return -EOPNOTSUPP;
r = getcon_raw(&mycon);
@@ -295,21 +314,27 @@ int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *
return r;
}
-void mac_selinux_free(char *label) {
+char* mac_selinux_free(char *label) {
#ifdef HAVE_SELINUX
- if (!mac_selinux_use())
- return;
+ if (!label)
+ return NULL;
+
+ if (!mac_selinux_have())
+ return NULL;
+
freecon((security_context_t) label);
#endif
+
+ return NULL;
}
int mac_selinux_create_file_prepare(const char *path, mode_t mode) {
- int r = 0;
#ifdef HAVE_SELINUX
_cleanup_security_context_free_ security_context_t filecon = NULL;
+ int r;
assert(path);
@@ -319,34 +344,33 @@ int mac_selinux_create_file_prepare(const char *path, mode_t mode) {
if (path_is_absolute(path))
r = selabel_lookup_raw(label_hnd, &filecon, path, mode);
else {
- _cleanup_free_ char *newpath;
+ _cleanup_free_ char *newpath = NULL;
- newpath = path_make_absolute_cwd(path);
- if (!newpath)
- return -ENOMEM;
+ r = path_make_absolute_cwd(path, &newpath);
+ if (r < 0)
+ return r;
r = selabel_lookup_raw(label_hnd, &filecon, newpath, mode);
}
- /* No context specified by the policy? Proceed without setting it. */
- if (r < 0 && errno == ENOENT)
- return 0;
+ if (r < 0) {
+ /* No context specified by the policy? Proceed without setting it. */
+ if (errno == ENOENT)
+ return 0;
- if (r < 0)
- r = -errno;
- else {
- r = setfscreatecon(filecon);
- if (r < 0) {
- log_enforcing("Failed to set SELinux security context %s for %s: %m", filecon, path);
- r = -errno;
- }
+ log_enforcing("Failed to determine SELinux security context for %s: %m", path);
+ } else {
+ if (setfscreatecon(filecon) >= 0)
+ return 0; /* Success! */
+
+ log_enforcing("Failed to set SELinux security context %s for %s: %m", filecon, path);
}
- if (r < 0 && security_getenforce() == 0)
- r = 0;
-#endif
+ if (security_getenforce() > 0)
+ return -errno;
- return r;
+#endif
+ return 0;
}
void mac_selinux_create_file_clear(void) {
@@ -399,6 +423,7 @@ int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
#ifdef HAVE_SELINUX
_cleanup_security_context_free_ security_context_t fcon = NULL;
const struct sockaddr_un *un;
+ bool context_changed = false;
char *path;
int r;
@@ -414,7 +439,7 @@ int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
goto skipped;
/* Filter out anonymous sockets */
- if (addrlen < sizeof(sa_family_t) + 1)
+ if (addrlen < offsetof(struct sockaddr_un, sun_path) + 1)
goto skipped;
/* Filter out abstract namespace sockets */
@@ -427,36 +452,44 @@ int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
if (path_is_absolute(path))
r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
else {
- _cleanup_free_ char *newpath;
+ _cleanup_free_ char *newpath = NULL;
- newpath = path_make_absolute_cwd(path);
- if (!newpath)
- return -ENOMEM;
+ r = path_make_absolute_cwd(path, &newpath);
+ if (r < 0)
+ return r;
r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK);
}
- if (r == 0)
- r = setfscreatecon(fcon);
+ if (r < 0) {
+ /* No context specified by the policy? Proceed without setting it */
+ if (errno == ENOENT)
+ goto skipped;
- if (r < 0 && errno != ENOENT) {
- log_enforcing("Failed to set SELinux security context %s for %s: %m", fcon, path);
+ log_enforcing("Failed to determine SELinux security context for %s: %m", path);
+ if (security_getenforce() > 0)
+ return -errno;
- if (security_getenforce() == 1) {
- r = -errno;
- goto finish;
- }
+ } else {
+ if (setfscreatecon(fcon) < 0) {
+ log_enforcing("Failed to set SELinux security context %s for %s: %m", fcon, path);
+ if (security_getenforce() > 0)
+ return -errno;
+ } else
+ context_changed = true;
}
- r = bind(fd, addr, addrlen);
- if (r < 0)
- r = -errno;
+ r = bind(fd, addr, addrlen) < 0 ? -errno : 0;
+
+ if (context_changed)
+ setfscreatecon(NULL);
-finish:
- setfscreatecon(NULL);
return r;
skipped:
#endif
- return bind(fd, addr, addrlen) < 0 ? -errno : 0;
+ if (bind(fd, addr, addrlen) < 0)
+ return -errno;
+
+ return 0;
}
diff --git a/src/basic/selinux-util.h b/src/basic/selinux-util.h
index 8467185291..0111f4c858 100644
--- a/src/basic/selinux-util.h
+++ b/src/basic/selinux-util.h
@@ -21,10 +21,14 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/socket.h>
#include <stdbool.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include "macro.h"
bool mac_selinux_use(void);
+bool mac_selinux_have(void);
void mac_selinux_retest(void);
int mac_selinux_init(const char *prefix);
@@ -36,7 +40,7 @@ int mac_selinux_apply(const char *path, const char *label);
int mac_selinux_get_create_label_from_exe(const char *exe, char **label);
int mac_selinux_get_our_label(char **label);
int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **label);
-void mac_selinux_free(char *label);
+char* mac_selinux_free(char *label);
int mac_selinux_create_file_prepare(const char *path, mode_t mode);
void mac_selinux_create_file_clear(void);
@@ -45,3 +49,5 @@ int mac_selinux_create_socket_prepare(const char *label);
void mac_selinux_create_socket_clear(void);
int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(char*, mac_selinux_free);
diff --git a/src/basic/set.h b/src/basic/set.h
index 4554ef2d49..5fd7de08f9 100644
--- a/src/basic/set.h
+++ b/src/basic/set.h
@@ -27,7 +27,6 @@
Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
#define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS)
-
static inline Set *set_free(Set *s) {
internal_hashmap_free(HASHMAP_BASE(s));
return NULL;
diff --git a/src/basic/sigbus.c b/src/basic/sigbus.c
index 0108603fe8..fe2e2d1a28 100644
--- a/src/basic/sigbus.c
+++ b/src/basic/sigbus.c
@@ -19,12 +19,14 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
#include <signal.h>
+#include <stddef.h>
#include <sys/mman.h>
#include "macro.h"
-#include "util.h"
#include "sigbus.h"
+#include "util.h"
#define SIGBUS_QUEUE_MAX 64
diff --git a/src/basic/signal-util.c b/src/basic/signal-util.c
index 90abe8af81..315efadd93 100644
--- a/src/basic/signal-util.c
+++ b/src/basic/signal-util.c
@@ -19,8 +19,16 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "macro.h"
+#include "parse-util.h"
#include "signal-util.h"
+#include "stdio-util.h"
+#include "string-table.h"
+#include "string-util.h"
int reset_all_signal_handlers(void) {
static const struct sigaction sa = {
@@ -227,9 +235,9 @@ const char *signal_to_string(int signo) {
return name;
if (signo >= SIGRTMIN && signo <= SIGRTMAX)
- snprintf(buf, sizeof(buf), "RTMIN+%d", signo - SIGRTMIN);
+ xsprintf(buf, "RTMIN+%d", signo - SIGRTMIN);
else
- snprintf(buf, sizeof(buf), "%d", signo);
+ xsprintf(buf, "%d", signo);
return buf;
}
@@ -266,3 +274,7 @@ int signal_from_string_try_harder(const char *s) {
return signo;
}
+
+void nop_signal_handler(int sig) {
+ /* nothing here */
+}
diff --git a/src/basic/signal-util.h b/src/basic/signal-util.h
index 5e6eb50b07..e7393e2dac 100644
--- a/src/basic/signal-util.h
+++ b/src/basic/signal-util.h
@@ -39,3 +39,5 @@ 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);
+
+void nop_signal_handler(int sig);
diff --git a/src/basic/siphash24.c b/src/basic/siphash24.c
index f68bd283a1..060e8ba387 100644
--- a/src/basic/siphash24.c
+++ b/src/basic/siphash24.c
@@ -13,123 +13,179 @@
this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
(Minimal changes made by Lennart Poettering, to make clean for inclusion in systemd)
+ (Refactored by Tom Gundersen to split up in several functions and follow systemd
+ coding style)
*/
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
+#include "macro.h"
#include "siphash24.h"
+#include "unaligned.h"
+
+static inline uint64_t rotate_left(uint64_t x, uint8_t b) {
+ assert(b < 64);
+
+ return (x << b) | (x >> (64 - b));
+}
+
+static inline void sipround(struct siphash *state) {
+ assert(state);
+
+ state->v0 += state->v1;
+ state->v1 = rotate_left(state->v1, 13);
+ state->v1 ^= state->v0;
+ state->v0 = rotate_left(state->v0, 32);
+ state->v2 += state->v3;
+ state->v3 = rotate_left(state->v3, 16);
+ state->v3 ^= state->v2;
+ state->v0 += state->v3;
+ state->v3 = rotate_left(state->v3, 21);
+ state->v3 ^= state->v0;
+ state->v2 += state->v1;
+ state->v1 = rotate_left(state->v1, 17);
+ state->v1 ^= state->v2;
+ state->v2 = rotate_left(state->v2, 32);
+}
+
+void siphash24_init(struct siphash *state, const uint8_t k[16]) {
+ uint64_t k0, k1;
+
+ assert(state);
+ assert(k);
+
+ k0 = unaligned_read_le64(k);
+ k1 = unaligned_read_le64(k + 8);
+
+ *state = (struct siphash) {
+ /* "somepseudorandomlygeneratedbytes" */
+ .v0 = 0x736f6d6570736575ULL ^ k0,
+ .v1 = 0x646f72616e646f6dULL ^ k1,
+ .v2 = 0x6c7967656e657261ULL ^ k0,
+ .v3 = 0x7465646279746573ULL ^ k1,
+ .padding = 0,
+ .inlen = 0,
+ };
+}
+
+void siphash24_compress(const void *_in, size_t inlen, struct siphash *state) {
+
+ const uint8_t *in = _in;
+ const uint8_t *end = in + inlen;
+ size_t left = state->inlen & 7;
+ uint64_t m;
+
+ assert(in);
+ assert(state);
+
+ /* Update total length */
+ state->inlen += inlen;
+
+ /* If padding exists, fill it out */
+ if (left > 0) {
+ for ( ; in < end && left < 8; in ++, left ++)
+ state->padding |= ((uint64_t) *in) << (left * 8);
+
+ if (in == end && left < 8)
+ /* We did not have enough input to fill out the padding completely */
+ return;
-typedef uint64_t u64;
-typedef uint32_t u32;
-typedef uint8_t u8;
-
-#define ROTL(x,b) (u64)( ((x) << (b)) | ( (x) >> (64 - (b))) )
-
-#define U32TO8_LE(p, v) \
- (p)[0] = (u8)((v) ); (p)[1] = (u8)((v) >> 8); \
- (p)[2] = (u8)((v) >> 16); (p)[3] = (u8)((v) >> 24);
-
-#define U64TO8_LE(p, v) \
- U32TO8_LE((p), (u32)((v) )); \
- U32TO8_LE((p) + 4, (u32)((v) >> 32));
-
-#define U8TO64_LE(p) \
- (((u64)((p)[0]) ) | \
- ((u64)((p)[1]) << 8) | \
- ((u64)((p)[2]) << 16) | \
- ((u64)((p)[3]) << 24) | \
- ((u64)((p)[4]) << 32) | \
- ((u64)((p)[5]) << 40) | \
- ((u64)((p)[6]) << 48) | \
- ((u64)((p)[7]) << 56))
-
-#define SIPROUND \
- do { \
- v0 += v1; v1=ROTL(v1,13); v1 ^= v0; v0=ROTL(v0,32); \
- v2 += v3; v3=ROTL(v3,16); v3 ^= v2; \
- v0 += v3; v3=ROTL(v3,21); v3 ^= v0; \
- v2 += v1; v1=ROTL(v1,17); v1 ^= v2; v2=ROTL(v2,32); \
- } while(0)
-
-/* SipHash-2-4 */
-void siphash24(uint8_t out[8], const void *_in, size_t inlen, const uint8_t k[16])
-{
- /* "somepseudorandomlygeneratedbytes" */
- u64 v0 = 0x736f6d6570736575ULL;
- u64 v1 = 0x646f72616e646f6dULL;
- u64 v2 = 0x6c7967656e657261ULL;
- u64 v3 = 0x7465646279746573ULL;
- u64 b;
- u64 k0 = U8TO64_LE( k );
- u64 k1 = U8TO64_LE( k + 8 );
- u64 m;
- const u8 *in = _in;
- const u8 *end = in + inlen - ( inlen % sizeof( u64 ) );
- const int left = inlen & 7;
- b = ( ( u64 )inlen ) << 56;
- v3 ^= k1;
- v2 ^= k0;
- v1 ^= k1;
- v0 ^= k0;
-
- for ( ; in != end; in += 8 )
- {
- m = U8TO64_LE( in );
#ifdef DEBUG
- printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 );
- printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 );
- printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 );
- printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 );
- printf( "(%3d) compress %08x %08x\n", ( int )inlen, ( u32 )( m >> 32 ), ( u32 )m );
+ printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0);
+ printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1);
+ printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2);
+ printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t) state->v3);
+ printf("(%3zu) compress padding %08x %08x\n", state->inlen, (uint32_t) (state->padding >> 32), (uint32_t)state->padding);
#endif
- v3 ^= m;
- SIPROUND;
- SIPROUND;
- v0 ^= m;
- }
-
- switch( left )
- {
- case 7: b |= ( ( u64 )in[ 6] ) << 48;
- case 6: b |= ( ( u64 )in[ 5] ) << 40;
+ state->v3 ^= state->padding;
+ sipround(state);
+ sipround(state);
+ state->v0 ^= state->padding;
- case 5: b |= ( ( u64 )in[ 4] ) << 32;
+ state->padding = 0;
+ }
- case 4: b |= ( ( u64 )in[ 3] ) << 24;
+ end -= (state->inlen % sizeof(uint64_t));
- case 3: b |= ( ( u64 )in[ 2] ) << 16;
+ for ( ; in < end; in += 8) {
+ m = unaligned_read_le64(in);
+#ifdef DEBUG
+ printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0);
+ printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1);
+ printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2);
+ printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t) state->v3);
+ printf("(%3zu) compress %08x %08x\n", state->inlen, (uint32_t) (m >> 32), (uint32_t) m);
+#endif
+ state->v3 ^= m;
+ sipround(state);
+ sipround(state);
+ state->v0 ^= m;
+ }
+
+ left = state->inlen & 7;
+ switch (left) {
+ case 7:
+ state->padding |= ((uint64_t) in[6]) << 48;
+ case 6:
+ state->padding |= ((uint64_t) in[5]) << 40;
+ case 5:
+ state->padding |= ((uint64_t) in[4]) << 32;
+ case 4:
+ state->padding |= ((uint64_t) in[3]) << 24;
+ case 3:
+ state->padding |= ((uint64_t) in[2]) << 16;
+ case 2:
+ state->padding |= ((uint64_t) in[1]) << 8;
+ case 1:
+ state->padding |= ((uint64_t) in[0]);
+ case 0:
+ break;
+ }
+}
- case 2: b |= ( ( u64 )in[ 1] ) << 8;
+uint64_t siphash24_finalize(struct siphash *state) {
+ uint64_t b;
- case 1: b |= ( ( u64 )in[ 0] ); break;
+ assert(state);
- case 0: break;
- }
+ b = state->padding | (((uint64_t) state->inlen) << 56);
#ifdef DEBUG
- printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 );
- printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 );
- printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 );
- printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 );
- printf( "(%3d) padding %08x %08x\n", ( int )inlen, ( u32 )( b >> 32 ), ( u32 )b );
+ printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0);
+ printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1);
+ printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2);
+ printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t) state->v3);
+ printf("(%3zu) padding %08x %08x\n", state->inlen, (uint32_t) (state->padding >> 32), (uint32_t) state->padding);
#endif
- v3 ^= b;
- SIPROUND;
- SIPROUND;
- v0 ^= b;
+
+ state->v3 ^= b;
+ sipround(state);
+ sipround(state);
+ state->v0 ^= b;
+
#ifdef DEBUG
- printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 );
- printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 );
- printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 );
- printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 );
+ printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0);
+ printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1);
+ printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2);
+ printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t) state->v3);
#endif
- v2 ^= 0xff;
- SIPROUND;
- SIPROUND;
- SIPROUND;
- SIPROUND;
- b = v0 ^ v1 ^ v2 ^ v3;
- U64TO8_LE( out, b );
+ state->v2 ^= 0xff;
+
+ sipround(state);
+ sipround(state);
+ sipround(state);
+ sipround(state);
+
+ return state->v0 ^ state->v1 ^ state->v2 ^ state->v3;
+}
+
+uint64_t siphash24(const void *in, size_t inlen, const uint8_t k[16]) {
+ struct siphash state;
+
+ assert(in);
+ assert(k);
+
+ siphash24_init(&state, k);
+ siphash24_compress(in, inlen, &state);
+
+ return siphash24_finalize(&state);
}
diff --git a/src/basic/siphash24.h b/src/basic/siphash24.h
index 62e1168a79..54e2420cc6 100644
--- a/src/basic/siphash24.h
+++ b/src/basic/siphash24.h
@@ -1,6 +1,23 @@
#pragma once
#include <inttypes.h>
+#include <stddef.h>
+#include <stdint.h>
#include <sys/types.h>
-void siphash24(uint8_t out[8], const void *in, size_t inlen, const uint8_t k[16]);
+struct siphash {
+ uint64_t v0;
+ uint64_t v1;
+ uint64_t v2;
+ uint64_t v3;
+ uint64_t padding;
+ size_t inlen;
+};
+
+void siphash24_init(struct siphash *state, const uint8_t k[16]);
+void siphash24_compress(const void *in, size_t inlen, struct siphash *state);
+#define siphash24_compress_byte(byte, state) siphash24_compress((const uint8_t[]) { (byte) }, 1, (state))
+
+uint64_t siphash24_finalize(struct siphash *state);
+
+uint64_t siphash24(const void *in, size_t inlen, const uint8_t k[16]);
diff --git a/src/basic/smack-util.c b/src/basic/smack-util.c
index 9e221d6eab..b9e4ff87d8 100644
--- a/src/basic/smack-util.c
+++ b/src/basic/smack-util.c
@@ -21,16 +21,21 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
#include <sys/xattr.h>
+#include <unistd.h>
-#include "util.h"
-#include "process-util.h"
-#include "path-util.h"
+#include "alloc-util.h"
#include "fileio.h"
+#include "log.h"
+#include "macro.h"
+#include "path-util.h"
+#include "process-util.h"
#include "smack-util.h"
-
-#define SMACK_FLOOR_LABEL "_"
-#define SMACK_STAR_LABEL "*"
+#include "string-table.h"
+#include "xattr-util.h"
#ifdef HAVE_SMACK
bool mac_smack_use(void) {
diff --git a/src/basic/smack-util.h b/src/basic/smack-util.h
index b3aa55eb8a..1d85b52a33 100644
--- a/src/basic/smack-util.h
+++ b/src/basic/smack-util.h
@@ -24,9 +24,13 @@
***/
#include <stdbool.h>
+#include <sys/types.h>
#include "macro.h"
+#define SMACK_FLOOR_LABEL "_"
+#define SMACK_STAR_LABEL "*"
+
typedef enum SmackAttr {
SMACK_ATTR_ACCESS = 0,
SMACK_ATTR_EXEC = 1,
diff --git a/src/basic/socket-label.c b/src/basic/socket-label.c
index 144e6fd86e..e169439e04 100644
--- a/src/basic/socket-label.c
+++ b/src/basic/socket-label.c
@@ -19,16 +19,22 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <string.h>
-#include <unistd.h>
#include <errno.h>
-#include <sys/stat.h>
+#include <netinet/in.h>
+#include <stdbool.h>
#include <stddef.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "log.h"
#include "macro.h"
-#include "util.h"
-#include "mkdir.h"
#include "missing.h"
+#include "mkdir.h"
#include "selinux-util.h"
#include "socket-util.h"
@@ -146,11 +152,8 @@ int make_socket_fd(int log_level, const char* address, int flags) {
int fd, r;
r = socket_address_parse(&a, address);
- if (r < 0) {
- log_error("Failed to parse socket address \"%s\": %s",
- address, strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse socket address \"%s\": %m", address);
fd = socket_address_listen(&a, flags, SOMAXCONN, SOCKET_ADDRESS_DEFAULT,
NULL, false, false, false, 0755, 0644, NULL);
diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c
index 8fd3149276..be144e157d 100644
--- a/src/basic/socket-util.c
+++ b/src/basic/socket-util.c
@@ -19,23 +19,33 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <string.h>
-#include <unistd.h>
-#include <errno.h>
#include <arpa/inet.h>
-#include <stdio.h>
+#include <errno.h>
+#include <limits.h>
#include <net/if.h>
-#include <sys/types.h>
-#include <stddef.h>
#include <netdb.h>
+#include <netinet/ip.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "formats-util.h"
+#include "log.h"
#include "macro.h"
+#include "missing.h"
+#include "parse-util.h"
#include "path-util.h"
-#include "util.h"
#include "socket-util.h"
-#include "missing.h"
-#include "fileio.h"
-#include "formats-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "user-util.h"
+#include "util.h"
int socket_address_parse(SocketAddress *a, const char *s) {
char *e, *n;
@@ -430,17 +440,10 @@ const char* socket_address_get_path(const SocketAddress *a) {
}
bool socket_ipv6_is_supported(void) {
- _cleanup_free_ char *l = NULL;
-
- if (access("/sys/module/ipv6", F_OK) != 0)
+ if (access("/proc/net/sockstat6", F_OK) != 0)
return false;
- /* If we can't check "disable" parameter, assume enabled */
- if (read_one_line_file("/sys/module/ipv6/parameters/disable", &l) < 0)
- return true;
-
- /* If module was loaded with disable=1 no IPv6 available */
- return l[0] == '0';
+ return true;
}
bool socket_address_matches_fd(const SocketAddress *a, int fd) {
@@ -749,21 +752,190 @@ bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b
return false;
}
-char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]) {
- assert(addr);
- assert(buffer);
+int fd_inc_sndbuf(int fd, size_t n) {
+ int r, value;
+ socklen_t l = sizeof(value);
+
+ r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l);
+ if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2)
+ return 0;
+
+ /* If we have the privileges we will ignore the kernel limit. */
+
+ value = (int) n;
+ if (setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, &value, sizeof(value)) < 0)
+ if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0)
+ return -errno;
+
+ return 1;
+}
+
+int fd_inc_rcvbuf(int fd, size_t n) {
+ int r, value;
+ socklen_t l = sizeof(value);
+
+ r = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, &l);
+ if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2)
+ return 0;
+
+ /* If we have the privileges we will ignore the kernel limit. */
+
+ value = (int) n;
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0)
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) < 0)
+ return -errno;
+ return 1;
+}
+
+static const char* const ip_tos_table[] = {
+ [IPTOS_LOWDELAY] = "low-delay",
+ [IPTOS_THROUGHPUT] = "throughput",
+ [IPTOS_RELIABILITY] = "reliability",
+ [IPTOS_LOWCOST] = "low-cost",
+};
+
+DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff);
+
+int getpeercred(int fd, struct ucred *ucred) {
+ socklen_t n = sizeof(struct ucred);
+ struct ucred u;
+ int r;
+
+ assert(fd >= 0);
+ assert(ucred);
+
+ r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &u, &n);
+ if (r < 0)
+ return -errno;
+
+ if (n != sizeof(struct ucred))
+ return -EIO;
+
+ /* Check if the data is actually useful and not suppressed due
+ * to namespacing issues */
+ if (u.pid <= 0)
+ return -ENODATA;
+ if (u.uid == UID_INVALID)
+ return -ENODATA;
+ if (u.gid == GID_INVALID)
+ return -ENODATA;
+
+ *ucred = u;
+ return 0;
+}
+
+int getpeersec(int fd, char **ret) {
+ socklen_t n = 64;
+ char *s;
+ int r;
- /* Like ether_ntoa() but uses %02x instead of %x to print
- * ethernet addresses, which makes them look less funny. Also,
- * doesn't use a static buffer. */
+ assert(fd >= 0);
+ assert(ret);
+
+ s = new0(char, n);
+ if (!s)
+ return -ENOMEM;
+
+ r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n);
+ if (r < 0) {
+ free(s);
+
+ if (errno != ERANGE)
+ return -errno;
+
+ s = new0(char, n);
+ if (!s)
+ return -ENOMEM;
+
+ r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n);
+ if (r < 0) {
+ free(s);
+ return -errno;
+ }
+ }
- sprintf(buffer, "%02x:%02x:%02x:%02x:%02x:%02x",
- addr->ether_addr_octet[0],
- addr->ether_addr_octet[1],
- addr->ether_addr_octet[2],
- addr->ether_addr_octet[3],
- addr->ether_addr_octet[4],
- addr->ether_addr_octet[5]);
+ if (isempty(s)) {
+ free(s);
+ return -EOPNOTSUPP;
+ }
+
+ *ret = s;
+ return 0;
+}
+
+int send_one_fd_sa(
+ int transport_fd,
+ int fd,
+ const struct sockaddr *sa, socklen_t len,
+ int flags) {
+
+ union {
+ struct cmsghdr cmsghdr;
+ uint8_t buf[CMSG_SPACE(sizeof(int))];
+ } control = {};
+ struct cmsghdr *cmsg;
+
+ struct msghdr mh = {
+ .msg_name = (struct sockaddr*) sa,
+ .msg_namelen = len,
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
+ };
+
+ assert(transport_fd >= 0);
+ assert(fd >= 0);
+
+ cmsg = CMSG_FIRSTHDR(&mh);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
+
+ mh.msg_controllen = CMSG_SPACE(sizeof(int));
+ if (sendmsg(transport_fd, &mh, MSG_NOSIGNAL | flags) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int receive_one_fd(int transport_fd, int flags) {
+ union {
+ struct cmsghdr cmsghdr;
+ uint8_t buf[CMSG_SPACE(sizeof(int))];
+ } control = {};
+ struct msghdr mh = {
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
+ };
+ struct cmsghdr *cmsg, *found = NULL;
+
+ assert(transport_fd >= 0);
+
+ /*
+ * Receive a single FD via @transport_fd. We don't care for
+ * the transport-type. We retrieve a single FD at most, so for
+ * packet-based transports, the caller must ensure to send
+ * only a single FD per packet. This is best used in
+ * combination with send_one_fd().
+ */
+
+ if (recvmsg(transport_fd, &mh, MSG_NOSIGNAL | MSG_CMSG_CLOEXEC | flags) < 0)
+ return -errno;
+
+ CMSG_FOREACH(cmsg, &mh) {
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_RIGHTS &&
+ cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
+ assert(!found);
+ found = cmsg;
+ break;
+ }
+ }
+
+ if (!found) {
+ cmsg_close_all(&mh);
+ return -EIO;
+ }
- return buffer;
+ return *(int*) CMSG_DATA(found);
}
diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h
index 6b0ce7836f..6da1df68d8 100644
--- a/src/basic/socket-util.h
+++ b/src/basic/socket-util.h
@@ -21,9 +21,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/socket.h>
-#include <netinet/in.h>
#include <netinet/ether.h>
+#include <netinet/in.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <sys/socket.h>
+#include <sys/types.h>
#include <sys/un.h>
#include <linux/netlink.h>
#include <linux/if_packet.h>
@@ -116,6 +119,21 @@ int netlink_family_from_string(const char *s) _pure_;
bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b);
-#define ETHER_ADDR_TO_STRING_MAX (3*6)
+int fd_inc_sndbuf(int fd, size_t n);
+int fd_inc_rcvbuf(int fd, size_t n);
+
+int ip_tos_to_string_alloc(int i, char **s);
+int ip_tos_from_string(const char *s);
+
+int getpeercred(int fd, struct ucred *ucred);
+int getpeersec(int fd, char **ret);
+
+int send_one_fd_sa(int transport_fd,
+ int fd,
+ const struct sockaddr *sa, socklen_t len,
+ int flags);
+#define send_one_fd(transport_fd, fd, flags) send_one_fd_sa(transport_fd, fd, NULL, 0, flags)
+int receive_one_fd(int transport_fd, int flags);
-char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]);
+#define CMSG_FOREACH(cmsg, mh) \
+ for ((cmsg) = CMSG_FIRSTHDR(mh); (cmsg); (cmsg) = CMSG_NXTHDR((mh), (cmsg)))
diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c
new file mode 100644
index 0000000000..21a8fb77a1
--- /dev/null
+++ b/src/basic/stat-util.c
@@ -0,0 +1,220 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010-2012 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <linux/magic.h>
+#include <sys/statvfs.h>
+#include <unistd.h>
+
+#include "dirent-util.h"
+#include "fd-util.h"
+#include "macro.h"
+#include "missing.h"
+#include "stat-util.h"
+#include "string-util.h"
+
+int is_symlink(const char *path) {
+ struct stat info;
+
+ assert(path);
+
+ if (lstat(path, &info) < 0)
+ return -errno;
+
+ return !!S_ISLNK(info.st_mode);
+}
+
+int is_dir(const char* path, bool follow) {
+ struct stat st;
+ int r;
+
+ assert(path);
+
+ if (follow)
+ r = stat(path, &st);
+ else
+ r = lstat(path, &st);
+ if (r < 0)
+ return -errno;
+
+ return !!S_ISDIR(st.st_mode);
+}
+
+int is_device_node(const char *path) {
+ struct stat info;
+
+ assert(path);
+
+ if (lstat(path, &info) < 0)
+ return -errno;
+
+ return !!(S_ISBLK(info.st_mode) || S_ISCHR(info.st_mode));
+}
+
+int dir_is_empty(const char *path) {
+ _cleanup_closedir_ DIR *d;
+ struct dirent *de;
+
+ d = opendir(path);
+ if (!d)
+ return -errno;
+
+ FOREACH_DIRENT(de, d, return -errno)
+ return 0;
+
+ return 1;
+}
+
+bool null_or_empty(struct stat *st) {
+ assert(st);
+
+ if (S_ISREG(st->st_mode) && st->st_size <= 0)
+ return true;
+
+ /* We don't want to hardcode the major/minor of /dev/null,
+ * hence we do a simpler "is this a device node?" check. */
+
+ if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode))
+ return true;
+
+ return false;
+}
+
+int null_or_empty_path(const char *fn) {
+ struct stat st;
+
+ assert(fn);
+
+ if (stat(fn, &st) < 0)
+ return -errno;
+
+ return null_or_empty(&st);
+}
+
+int null_or_empty_fd(int fd) {
+ struct stat st;
+
+ assert(fd >= 0);
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ return null_or_empty(&st);
+}
+
+int path_is_read_only_fs(const char *path) {
+ struct statvfs st;
+
+ assert(path);
+
+ if (statvfs(path, &st) < 0)
+ return -errno;
+
+ if (st.f_flag & ST_RDONLY)
+ return true;
+
+ /* On NFS, statvfs() might not reflect whether we can actually
+ * write to the remote share. Let's try again with
+ * access(W_OK) which is more reliable, at least sometimes. */
+ if (access(path, W_OK) < 0 && errno == EROFS)
+ return true;
+
+ return false;
+}
+
+int path_is_os_tree(const char *path) {
+ char *p;
+ int r;
+
+ assert(path);
+
+ /* We use /usr/lib/os-release as flag file if something is an OS */
+ p = strjoina(path, "/usr/lib/os-release");
+ r = access(p, F_OK);
+ if (r >= 0)
+ return 1;
+
+ /* Also check for the old location in /etc, just in case. */
+ p = strjoina(path, "/etc/os-release");
+ r = access(p, F_OK);
+
+ return r >= 0;
+}
+
+int files_same(const char *filea, const char *fileb) {
+ struct stat a, b;
+
+ assert(filea);
+ assert(fileb);
+
+ if (stat(filea, &a) < 0)
+ return -errno;
+
+ if (stat(fileb, &b) < 0)
+ return -errno;
+
+ return a.st_dev == b.st_dev &&
+ a.st_ino == b.st_ino;
+}
+
+bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) {
+ assert(s);
+ assert_cc(sizeof(statfs_f_type_t) >= sizeof(s->f_type));
+
+ return F_TYPE_EQUAL(s->f_type, magic_value);
+}
+
+int fd_check_fstype(int fd, statfs_f_type_t magic_value) {
+ struct statfs s;
+
+ if (fstatfs(fd, &s) < 0)
+ return -errno;
+
+ return is_fs_type(&s, magic_value);
+}
+
+int path_check_fstype(const char *path, statfs_f_type_t magic_value) {
+ _cleanup_close_ int fd = -1;
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return -errno;
+
+ return fd_check_fstype(fd, magic_value);
+}
+
+bool is_temporary_fs(const struct statfs *s) {
+ return is_fs_type(s, TMPFS_MAGIC) ||
+ is_fs_type(s, RAMFS_MAGIC);
+}
+
+int fd_is_temporary_fs(int fd) {
+ struct statfs s;
+
+ if (fstatfs(fd, &s) < 0)
+ return -errno;
+
+ return is_temporary_fs(&s);
+}
diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h
new file mode 100644
index 0000000000..8e031e6155
--- /dev/null
+++ b/src/basic/stat-util.h
@@ -0,0 +1,71 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010-2012 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <sys/types.h>
+#include <sys/vfs.h>
+
+#include "macro.h"
+
+int is_symlink(const char *path);
+int is_dir(const char *path, bool follow);
+int is_device_node(const char *path);
+
+int dir_is_empty(const char *path);
+
+static inline int dir_is_populated(const char *path) {
+ int r;
+ r = dir_is_empty(path);
+ if (r < 0)
+ return r;
+ return !r;
+}
+
+bool null_or_empty(struct stat *st) _pure_;
+int null_or_empty_path(const char *fn);
+int null_or_empty_fd(int fd);
+
+int path_is_read_only_fs(const char *path);
+int path_is_os_tree(const char *path);
+
+int files_same(const char *filea, const char *fileb);
+
+/* The .f_type field of struct statfs is really weird defined on
+ * different archs. Let's give its type a name. */
+typedef typeof(((struct statfs*)NULL)->f_type) statfs_f_type_t;
+
+bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) _pure_;
+int fd_check_fstype(int fd, statfs_f_type_t magic_value);
+int path_check_fstype(const char *path, statfs_f_type_t magic_value);
+
+bool is_temporary_fs(const struct statfs *s) _pure_;
+int fd_is_temporary_fs(int fd);
+
+/* Because statfs.t_type can be int on some architectures, we have to cast
+ * the const magic to the type, otherwise the compiler warns about
+ * signed/unsigned comparison, because the magic can be 32 bit unsigned.
+ */
+#define F_TYPE_EQUAL(a, b) (a == (typeof(a)) b)
diff --git a/src/basic/stdio-util.h b/src/basic/stdio-util.h
new file mode 100644
index 0000000000..b36e8a947e
--- /dev/null
+++ b/src/basic/stdio-util.h
@@ -0,0 +1,78 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <printf.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "macro.h"
+
+#define xsprintf(buf, fmt, ...) \
+ assert_message_se((size_t) snprintf(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__) < ELEMENTSOF(buf), "xsprintf: " #buf "[] must be big enough")
+
+
+#define VA_FORMAT_ADVANCE(format, ap) \
+do { \
+ int _argtypes[128]; \
+ size_t _i, _k; \
+ _k = parse_printf_format((format), ELEMENTSOF(_argtypes), _argtypes); \
+ assert(_k < ELEMENTSOF(_argtypes)); \
+ for (_i = 0; _i < _k; _i++) { \
+ if (_argtypes[_i] & PA_FLAG_PTR) { \
+ (void) va_arg(ap, void*); \
+ continue; \
+ } \
+ \
+ switch (_argtypes[_i]) { \
+ case PA_INT: \
+ case PA_INT|PA_FLAG_SHORT: \
+ case PA_CHAR: \
+ (void) va_arg(ap, int); \
+ break; \
+ case PA_INT|PA_FLAG_LONG: \
+ (void) va_arg(ap, long int); \
+ break; \
+ case PA_INT|PA_FLAG_LONG_LONG: \
+ (void) va_arg(ap, long long int); \
+ break; \
+ case PA_WCHAR: \
+ (void) va_arg(ap, wchar_t); \
+ break; \
+ case PA_WSTRING: \
+ case PA_STRING: \
+ case PA_POINTER: \
+ (void) va_arg(ap, void*); \
+ break; \
+ case PA_FLOAT: \
+ case PA_DOUBLE: \
+ (void) va_arg(ap, double); \
+ break; \
+ case PA_DOUBLE|PA_FLAG_LONG_DOUBLE: \
+ (void) va_arg(ap, long double); \
+ break; \
+ default: \
+ assert_not_reached("Unknown format string argument."); \
+ } \
+ } \
+} while(false)
diff --git a/src/basic/strbuf.c b/src/basic/strbuf.c
index 01a076c2ba..c9984bef08 100644
--- a/src/basic/strbuf.c
+++ b/src/basic/strbuf.c
@@ -19,10 +19,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
#include <stdlib.h>
#include <string.h>
-#include "util.h"
+#include "alloc-util.h"
#include "strbuf.h"
/*
diff --git a/src/basic/strbuf.h b/src/basic/strbuf.h
index fbc4e5f2a1..69565f7e2c 100644
--- a/src/basic/strbuf.h
+++ b/src/basic/strbuf.h
@@ -21,7 +21,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <stddef.h>
#include <stdint.h>
+#include <sys/types.h>
struct strbuf {
char *buf;
diff --git a/src/libsystemd/sd-resolve/resolve-util.h b/src/basic/string-table.c
index 019cdaffe1..4633a57f44 100644
--- a/src/libsystemd/sd-resolve/resolve-util.h
+++ b/src/basic/string-table.c
@@ -1,11 +1,9 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-#pragma once
-
/***
This file is part of systemd.
- Copyright 2014 Daniel Buch
+ 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
@@ -21,11 +19,18 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
-#include "sd-resolve.h"
+#include "string-table.h"
+#include "string-util.h"
+
+ssize_t string_table_lookup(const char * const *table, size_t len, const char *key) {
+ size_t i;
+
+ if (!key)
+ return -1;
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_resolve*, sd_resolve_unref);
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_resolve_query*, sd_resolve_query_unref);
+ for (i = 0; i < len; ++i)
+ if (streq_ptr(table[i], key))
+ return (ssize_t) i;
-#define _cleanup_resolve_unref_ _cleanup_(sd_resolve_unrefp)
-#define _cleanup_resolve_query_unref_ _cleanup_(sd_resolve_query_unrefp)
+ return -1;
+}
diff --git a/src/basic/string-table.h b/src/basic/string-table.h
new file mode 100644
index 0000000000..588404ab5a
--- /dev/null
+++ b/src/basic/string-table.h
@@ -0,0 +1,107 @@
+
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "macro.h"
+#include "parse-util.h"
+#include "string-util.h"
+
+ssize_t string_table_lookup(const char * const *table, size_t len, const char *key);
+
+/* 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) { \
+ if (i < 0 || i >= (type) ELEMENTSOF(name##_table)) \
+ return NULL; \
+ return name##_table[i]; \
+ }
+
+#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) \
+ scope type name##_from_string(const char *s) { \
+ return (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \
+ }
+
+#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope) \
+ scope type name##_from_string(const char *s) { \
+ int b; \
+ b = parse_boolean(s); \
+ if (b == 0) \
+ return (type) 0; \
+ else if (b > 0) \
+ return yes; \
+ return (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \
+ }
+
+#define _DEFINE_STRING_TABLE_LOOKUP(name,type,scope) \
+ _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \
+ _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) \
+ struct __useless_struct_to_allow_trailing_semicolon__
+
+#define _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,scope) \
+ _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \
+ _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope) \
+ struct __useless_struct_to_allow_trailing_semicolon__
+
+#define DEFINE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,)
+#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,static)
+#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,static)
+#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,static)
+
+#define DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes) _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,)
+
+/* For string conversions where numbers are also acceptable */
+#define DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(name,type,max) \
+ int name##_to_string_alloc(type i, char **str) { \
+ char *s; \
+ if (i < 0 || i > max) \
+ return -ERANGE; \
+ if (i < (type) ELEMENTSOF(name##_table)) { \
+ s = strdup(name##_table[i]); \
+ if (!s) \
+ return -ENOMEM; \
+ } else { \
+ if (asprintf(&s, "%i", i) < 0) \
+ return -ENOMEM; \
+ } \
+ *str = s; \
+ return 0; \
+ } \
+ type name##_from_string(const char *s) { \
+ type i; \
+ unsigned u = 0; \
+ if (!s) \
+ return (type) -1; \
+ for (i = 0; i < (type) ELEMENTSOF(name##_table); i++) \
+ if (streq_ptr(name##_table[i], s)) \
+ return i; \
+ if (safe_atou(s, &u) >= 0 && u <= max) \
+ return (type) u; \
+ return (type) -1; \
+ } \
+ struct __useless_struct_to_allow_trailing_semicolon__
diff --git a/src/basic/string-util.c b/src/basic/string-util.c
new file mode 100644
index 0000000000..1f95a9abba
--- /dev/null
+++ b/src/basic/string-util.c
@@ -0,0 +1,856 @@
+/*-*- 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 <errno.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "alloc-util.h"
+#include "gunicode.h"
+#include "macro.h"
+#include "string-util.h"
+#include "utf8.h"
+#include "util.h"
+
+int strcmp_ptr(const char *a, const char *b) {
+
+ /* Like strcmp(), but tries to make sense of NULL pointers */
+ if (a && b)
+ return strcmp(a, b);
+
+ if (!a && b)
+ return -1;
+
+ if (a && !b)
+ return 1;
+
+ return 0;
+}
+
+char* endswith(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 (memcmp(s + sl - pl, postfix, pl) != 0)
+ return NULL;
+
+ 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;
+
+ assert(s);
+ assert(word);
+
+ /* Checks if the string starts with the specified word, either
+ * followed by NUL or by whitespace. Returns a pointer to the
+ * NUL or the first character after the whitespace. */
+
+ sl = strlen(s);
+ wl = strlen(word);
+
+ if (sl < wl)
+ return NULL;
+
+ if (wl == 0)
+ return (char*) s;
+
+ if (memcmp(s, word, wl) != 0)
+ return NULL;
+
+ p = s + wl;
+ if (*p == 0)
+ return (char*) p;
+
+ if (!strchr(WHITESPACE, *p))
+ return NULL;
+
+ p += strspn(p, WHITESPACE);
+ return (char*) p;
+}
+
+static size_t strcspn_escaped(const char *s, const char *reject) {
+ bool escaped = false;
+ int n;
+
+ for (n=0; s[n]; n++) {
+ if (escaped)
+ escaped = false;
+ else if (s[n] == '\\')
+ escaped = true;
+ else if (strchr(reject, s[n]))
+ break;
+ }
+
+ /* if s ends in \, return index of previous char */
+ return n - escaped;
+}
+
+/* Split a string into words. */
+const char* split(const char **state, size_t *l, const char *separator, bool quoted) {
+ const char *current;
+
+ current = *state;
+
+ if (!*current) {
+ assert(**state == '\0');
+ return NULL;
+ }
+
+ current += strspn(current, separator);
+ if (!*current) {
+ *state = current;
+ return NULL;
+ }
+
+ if (quoted && strchr("\'\"", *current)) {
+ char quotechars[2] = {*current, '\0'};
+
+ *l = strcspn_escaped(current + 1, quotechars);
+ 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;
+ }
+ *state = current++ + *l + 2;
+ } else if (quoted) {
+ *l = strcspn_escaped(current, separator);
+ if (current[*l] && !strchr(separator, current[*l])) {
+ /* unfinished escape */
+ *state = current;
+ return NULL;
+ }
+ *state = current + *l;
+ } else {
+ *l = strcspn(current, separator);
+ *state = current + *l;
+ }
+
+ return current;
+}
+
+char *strnappend(const char *s, const char *suffix, size_t b) {
+ size_t a;
+ char *r;
+
+ if (!s && !suffix)
+ return strdup("");
+
+ if (!s)
+ return strndup(suffix, b);
+
+ if (!suffix)
+ return strdup(s);
+
+ assert(s);
+ assert(suffix);
+
+ a = strlen(s);
+ if (b > ((size_t) -1) - a)
+ return NULL;
+
+ r = new(char, a+b+1);
+ if (!r)
+ return NULL;
+
+ memcpy(r, s, a);
+ memcpy(r+a, suffix, b);
+ r[a+b] = 0;
+
+ return r;
+}
+
+char *strappend(const char *s, const char *suffix) {
+ return strnappend(s, suffix, suffix ? strlen(suffix) : 0);
+}
+
+char *strjoin(const char *x, ...) {
+ va_list ap;
+ size_t l;
+ char *r, *p;
+
+ va_start(ap, x);
+
+ if (x) {
+ l = strlen(x);
+
+ for (;;) {
+ const char *t;
+ size_t n;
+
+ t = va_arg(ap, const char *);
+ if (!t)
+ break;
+
+ n = strlen(t);
+ if (n > ((size_t) -1) - l) {
+ va_end(ap);
+ return NULL;
+ }
+
+ l += n;
+ }
+ } else
+ l = 0;
+
+ va_end(ap);
+
+ r = new(char, l+1);
+ if (!r)
+ return NULL;
+
+ if (x) {
+ p = stpcpy(r, x);
+
+ va_start(ap, x);
+
+ for (;;) {
+ const char *t;
+
+ t = va_arg(ap, const char *);
+ if (!t)
+ break;
+
+ p = stpcpy(p, t);
+ }
+
+ va_end(ap);
+ } else
+ r[0] = 0;
+
+ return r;
+}
+
+char *strstrip(char *s) {
+ char *e;
+
+ /* Drops trailing whitespace. Modifies the string in
+ * place. Returns pointer to first non-space character */
+
+ s += strspn(s, WHITESPACE);
+
+ for (e = strchr(s, 0); e > s; e --)
+ if (!strchr(WHITESPACE, e[-1]))
+ break;
+
+ *e = 0;
+
+ return s;
+}
+
+char *delete_chars(char *s, const char *bad) {
+ char *f, *t;
+
+ /* Drops all whitespace, regardless where in the string */
+
+ for (f = s, t = s; *f; f++) {
+ if (strchr(bad, *f))
+ continue;
+
+ *(t++) = *f;
+ }
+
+ *t = 0;
+
+ return s;
+}
+
+char *truncate_nl(char *s) {
+ assert(s);
+
+ s[strcspn(s, NEWLINE)] = 0;
+ return s;
+}
+
+char ascii_tolower(char x) {
+
+ if (x >= 'A' && x <= 'Z')
+ return x - 'A' + 'a';
+
+ return x;
+}
+
+char *ascii_strlower(char *t) {
+ char *p;
+
+ assert(t);
+
+ for (p = t; *p; p++)
+ *p = ascii_tolower(*p);
+
+ return t;
+}
+
+char *ascii_strlower_n(char *t, size_t n) {
+ size_t i;
+
+ if (n <= 0)
+ return t;
+
+ for (i = 0; i < n; i++)
+ t[i] = ascii_tolower(t[i]);
+
+ return t;
+}
+
+int ascii_strcasecmp_n(const char *a, const char *b, size_t n) {
+
+ for (; n > 0; a++, b++, n--) {
+ int x, y;
+
+ x = (int) (uint8_t) ascii_tolower(*a);
+ y = (int) (uint8_t) ascii_tolower(*b);
+
+ if (x != y)
+ return x - y;
+ }
+
+ return 0;
+}
+
+int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m) {
+ int r;
+
+ r = ascii_strcasecmp_n(a, b, MIN(n, m));
+ if (r != 0)
+ return r;
+
+ if (n < m)
+ return -1;
+ else if (n > m)
+ return 1;
+ else
+ return 0;
+}
+
+bool chars_intersect(const char *a, const char *b) {
+ const char *p;
+
+ /* Returns true if any of the chars in a are in b. */
+ for (p = a; *p; p++)
+ if (strchr(b, *p))
+ return true;
+
+ return false;
+}
+
+bool string_has_cc(const char *p, const char *ok) {
+ const char *t;
+
+ assert(p);
+
+ /*
+ * Check if a string contains control characters. If 'ok' is
+ * non-NULL it may be a string containing additional CCs to be
+ * considered OK.
+ */
+
+ for (t = p; *t; t++) {
+ if (ok && strchr(ok, *t))
+ continue;
+
+ if (*t > 0 && *t < ' ')
+ return true;
+
+ if (*t == 127)
+ return true;
+ }
+
+ return false;
+}
+
+static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
+ size_t x;
+ char *r;
+
+ assert(s);
+ assert(percent <= 100);
+ assert(new_length >= 3);
+
+ if (old_length <= 3 || old_length <= new_length)
+ return strndup(s, old_length);
+
+ r = new0(char, new_length+1);
+ if (!r)
+ return NULL;
+
+ x = (new_length * percent) / 100;
+
+ if (x > new_length - 3)
+ x = new_length - 3;
+
+ memcpy(r, s, x);
+ r[x] = '.';
+ r[x+1] = '.';
+ r[x+2] = '.';
+ memcpy(r + x + 3,
+ s + old_length - (new_length - x - 3),
+ new_length - x - 3);
+
+ return r;
+}
+
+char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
+ size_t x;
+ char *e;
+ const char *i, *j;
+ unsigned k, len, len2;
+
+ assert(s);
+ assert(percent <= 100);
+ assert(new_length >= 3);
+
+ /* if no multibyte characters use ascii_ellipsize_mem for speed */
+ if (ascii_is_valid(s))
+ return ascii_ellipsize_mem(s, old_length, new_length, percent);
+
+ if (old_length <= 3 || old_length <= new_length)
+ return strndup(s, old_length);
+
+ x = (new_length * percent) / 100;
+
+ if (x > new_length - 3)
+ x = new_length - 3;
+
+ k = 0;
+ for (i = s; k < x && i < s + old_length; i = utf8_next_char(i)) {
+ int c;
+
+ c = utf8_encoded_to_unichar(i);
+ if (c < 0)
+ return NULL;
+ k += unichar_iswide(c) ? 2 : 1;
+ }
+
+ if (k > x) /* last character was wide and went over quota */
+ x ++;
+
+ for (j = s + old_length; k < new_length && j > i; ) {
+ int c;
+
+ j = utf8_prev_char(j);
+ c = utf8_encoded_to_unichar(j);
+ if (c < 0)
+ return NULL;
+ k += unichar_iswide(c) ? 2 : 1;
+ }
+ assert(i <= j);
+
+ /* we don't actually need to ellipsize */
+ if (i == j)
+ return memdup(s, old_length + 1);
+
+ /* make space for ellipsis */
+ j = utf8_next_char(j);
+
+ len = i - s;
+ len2 = s + old_length - j;
+ e = new(char, len + 3 + len2 + 1);
+ if (!e)
+ return NULL;
+
+ /*
+ printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n",
+ old_length, new_length, x, len, len2, k);
+ */
+
+ memcpy(e, s, len);
+ e[len] = 0xe2; /* tri-dot ellipsis: … */
+ e[len + 1] = 0x80;
+ e[len + 2] = 0xa6;
+
+ memcpy(e + len + 3, j, len2 + 1);
+
+ return e;
+}
+
+char *ellipsize(const char *s, size_t length, unsigned percent) {
+ return ellipsize_mem(s, strlen(s), length, percent);
+}
+
+bool nulstr_contains(const char*nulstr, const char *needle) {
+ const char *i;
+
+ if (!nulstr)
+ return false;
+
+ NULSTR_FOREACH(i, nulstr)
+ if (streq(i, needle))
+ return true;
+
+ return false;
+}
+
+char* strshorten(char *s, size_t l) {
+ assert(s);
+
+ if (l < strlen(s))
+ s[l] = 0;
+
+ return s;
+}
+
+char *strreplace(const char *text, const char *old_string, const char *new_string) {
+ const char *f;
+ char *t, *r;
+ size_t l, old_len, new_len;
+
+ assert(text);
+ assert(old_string);
+ assert(new_string);
+
+ old_len = strlen(old_string);
+ new_len = strlen(new_string);
+
+ l = strlen(text);
+ r = new(char, l+1);
+ if (!r)
+ return NULL;
+
+ f = text;
+ t = r;
+ while (*f) {
+ char *a;
+ size_t d, nl;
+
+ if (!startswith(f, old_string)) {
+ *(t++) = *(f++);
+ continue;
+ }
+
+ d = t - r;
+ nl = l - old_len + new_len;
+ a = realloc(r, nl + 1);
+ if (!a)
+ goto oom;
+
+ l = nl;
+ r = a;
+ t = r + d;
+
+ t = stpcpy(t, new_string);
+ f += old_len;
+ }
+
+ *t = 0;
+ return r;
+
+oom:
+ free(r);
+ return NULL;
+}
+
+char *strip_tab_ansi(char **ibuf, size_t *_isz) {
+ const char *i, *begin = NULL;
+ enum {
+ STATE_OTHER,
+ STATE_ESCAPE,
+ STATE_BRACKET
+ } state = STATE_OTHER;
+ char *obuf = NULL;
+ size_t osz = 0, isz;
+ FILE *f;
+
+ assert(ibuf);
+ assert(*ibuf);
+
+ /* Strips ANSI color and replaces TABs by 8 spaces */
+
+ isz = _isz ? *_isz : strlen(*ibuf);
+
+ f = open_memstream(&obuf, &osz);
+ if (!f)
+ return NULL;
+
+ for (i = *ibuf; i < *ibuf + isz + 1; i++) {
+
+ switch (state) {
+
+ case STATE_OTHER:
+ if (i >= *ibuf + isz) /* EOT */
+ break;
+ else if (*i == '\x1B')
+ state = STATE_ESCAPE;
+ else if (*i == '\t')
+ fputs(" ", f);
+ else
+ fputc(*i, f);
+ break;
+
+ case STATE_ESCAPE:
+ if (i >= *ibuf + isz) { /* EOT */
+ fputc('\x1B', f);
+ break;
+ } else if (*i == '[') {
+ state = STATE_BRACKET;
+ begin = i + 1;
+ } else {
+ fputc('\x1B', f);
+ fputc(*i, f);
+ state = STATE_OTHER;
+ }
+
+ break;
+
+ case STATE_BRACKET:
+
+ if (i >= *ibuf + isz || /* EOT */
+ (!(*i >= '0' && *i <= '9') && *i != ';' && *i != 'm')) {
+ fputc('\x1B', f);
+ fputc('[', f);
+ state = STATE_OTHER;
+ i = begin-1;
+ } else if (*i == 'm')
+ state = STATE_OTHER;
+ break;
+ }
+ }
+
+ if (ferror(f)) {
+ fclose(f);
+ free(obuf);
+ return NULL;
+ }
+
+ fclose(f);
+
+ free(*ibuf);
+ *ibuf = obuf;
+
+ if (_isz)
+ *_isz = osz;
+
+ return obuf;
+}
+
+char *strextend(char **x, ...) {
+ va_list ap;
+ size_t f, l;
+ char *r, *p;
+
+ assert(x);
+
+ l = f = *x ? strlen(*x) : 0;
+
+ va_start(ap, x);
+ for (;;) {
+ const char *t;
+ size_t n;
+
+ t = va_arg(ap, const char *);
+ if (!t)
+ break;
+
+ n = strlen(t);
+ if (n > ((size_t) -1) - l) {
+ va_end(ap);
+ return NULL;
+ }
+
+ l += n;
+ }
+ va_end(ap);
+
+ r = realloc(*x, l+1);
+ if (!r)
+ return NULL;
+
+ p = r + f;
+
+ va_start(ap, x);
+ for (;;) {
+ const char *t;
+
+ t = va_arg(ap, const char *);
+ if (!t)
+ break;
+
+ p = stpcpy(p, t);
+ }
+ va_end(ap);
+
+ *p = 0;
+ *x = r;
+
+ return r + l;
+}
+
+char *strrep(const char *s, unsigned n) {
+ size_t l;
+ char *r, *p;
+ unsigned i;
+
+ assert(s);
+
+ l = strlen(s);
+ p = r = malloc(l * n + 1);
+ if (!r)
+ return NULL;
+
+ for (i = 0; i < n; i++)
+ p = stpcpy(p, s);
+
+ *p = 0;
+ return r;
+}
+
+int split_pair(const char *s, const char *sep, char **l, char **r) {
+ char *x, *a, *b;
+
+ assert(s);
+ assert(sep);
+ assert(l);
+ assert(r);
+
+ if (isempty(sep))
+ return -EINVAL;
+
+ x = strstr(s, sep);
+ if (!x)
+ return -EINVAL;
+
+ a = strndup(s, x - s);
+ if (!a)
+ return -ENOMEM;
+
+ b = strdup(x + strlen(sep));
+ if (!b) {
+ free(a);
+ return -ENOMEM;
+ }
+
+ *l = a;
+ *r = b;
+
+ return 0;
+}
+
+int free_and_strdup(char **p, const char *s) {
+ char *t;
+
+ assert(p);
+
+ /* 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)
+ return -ENOMEM;
+ } else
+ t = NULL;
+
+ free(*p);
+ *p = t;
+
+ return 1;
+}
+
+#pragma GCC push_options
+#pragma GCC optimize("O0")
+
+void* memory_erase(void *p, size_t l) {
+ volatile uint8_t* x = (volatile uint8_t*) p;
+
+ /* This basically does what memset() does, but hopefully isn't
+ * optimized away by the compiler. One of those days, when
+ * glibc learns memset_s() we should replace this call by
+ * memset_s(), but until then this has to do. */
+
+ for (; l > 0; l--)
+ *(x++) = 'x';
+
+ return p;
+}
+
+#pragma GCC pop_options
+
+char* string_erase(char *x) {
+
+ if (!x)
+ return NULL;
+
+ /* A delicious drop of snake-oil! To be called on memory where
+ * we stored passphrases or so, after we used them. */
+
+ return memory_erase(x, strlen(x));
+}
+
+char *string_free_erase(char *s) {
+ return mfree(string_erase(s));
+}
+
+bool string_is_safe(const char *p) {
+ const char *t;
+
+ if (!p)
+ return false;
+
+ for (t = p; *t; t++) {
+ if (*t > 0 && *t < ' ') /* no control characters */
+ return false;
+
+ if (strchr(QUOTES "\\\x7f", *t))
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/basic/string-util.h b/src/basic/string-util.h
new file mode 100644
index 0000000000..8ea18f45aa
--- /dev/null
+++ b/src/basic/string-util.h
@@ -0,0 +1,191 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <alloca.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "macro.h"
+
+/* What is interpreted as whitespace? */
+#define WHITESPACE " \t\n\r"
+#define NEWLINE "\n\r"
+#define QUOTES "\"\'"
+#define COMMENTS "#;"
+#define GLOB_CHARS "*?["
+#define DIGITS "0123456789"
+#define LOWERCASE_LETTERS "abcdefghijklmnopqrstuvwxyz"
+#define UPPERCASE_LETTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+#define LETTERS LOWERCASE_LETTERS UPPERCASE_LETTERS
+#define ALPHANUMERICAL LETTERS DIGITS
+
+#define streq(a,b) (strcmp((a),(b)) == 0)
+#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0)
+#define strcaseeq(a,b) (strcasecmp((a),(b)) == 0)
+#define strncaseeq(a, b, n) (strncasecmp((a), (b), (n)) == 0)
+
+int strcmp_ptr(const char *a, const char *b) _pure_;
+
+static inline bool streq_ptr(const char *a, const char *b) {
+ return strcmp_ptr(a, b) == 0;
+}
+
+static inline const char* strempty(const char *s) {
+ return s ? s : "";
+}
+
+static inline const char* strnull(const char *s) {
+ return s ? s : "(null)";
+}
+
+static inline const char *strna(const char *s) {
+ return s ? s : "n/a";
+}
+
+static inline bool isempty(const char *p) {
+ return !p || !p[0];
+}
+
+static inline char *startswith(const char *s, const char *prefix) {
+ size_t l;
+
+ l = strlen(prefix);
+ if (strncmp(s, prefix, l) == 0)
+ return (char*) s + l;
+
+ return NULL;
+}
+
+static inline char *startswith_no_case(const char *s, const char *prefix) {
+ size_t l;
+
+ l = strlen(prefix);
+ if (strncasecmp(s, prefix, l) == 0)
+ return (char*) s + l;
+
+ return NULL;
+}
+
+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_;
+
+const char* split(const char **state, size_t *l, const char *separator, bool quoted);
+
+#define FOREACH_WORD(word, length, s, state) \
+ _FOREACH_WORD(word, length, s, WHITESPACE, false, state)
+
+#define FOREACH_WORD_SEPARATOR(word, length, s, separator, state) \
+ _FOREACH_WORD(word, length, s, separator, false, state)
+
+#define FOREACH_WORD_QUOTED(word, length, s, state) \
+ _FOREACH_WORD(word, length, s, WHITESPACE, true, state)
+
+#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)))
+
+char *strappend(const char *s, const char *suffix);
+char *strnappend(const char *s, const char *suffix, size_t length);
+
+char *strjoin(const char *x, ...) _sentinel_;
+
+#define strjoina(a, ...) \
+ ({ \
+ const char *_appendees_[] = { a, __VA_ARGS__ }; \
+ char *_d_, *_p_; \
+ int _len_ = 0; \
+ unsigned _i_; \
+ for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_) && _appendees_[_i_]; _i_++) \
+ _len_ += strlen(_appendees_[_i_]); \
+ _p_ = _d_ = alloca(_len_ + 1); \
+ for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_) && _appendees_[_i_]; _i_++) \
+ _p_ = stpcpy(_p_, _appendees_[_i_]); \
+ *_p_ = 0; \
+ _d_; \
+ })
+
+char *strstrip(char *s);
+char *delete_chars(char *s, const char *bad);
+char *truncate_nl(char *s);
+
+char ascii_tolower(char x);
+char *ascii_strlower(char *s);
+char *ascii_strlower_n(char *s, size_t n);
+
+int ascii_strcasecmp_n(const char *a, const char *b, size_t n);
+int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m);
+
+bool chars_intersect(const char *a, const char *b) _pure_;
+
+static inline bool _pure_ in_charset(const char *s, const char* charset) {
+ assert(s);
+ assert(charset);
+ return s[strspn(s, charset)] == '\0';
+}
+
+bool string_has_cc(const char *p, const char *ok) _pure_;
+
+char *ellipsize_mem(const char *s, size_t old_length_bytes, size_t new_length_columns, unsigned percent);
+char *ellipsize(const char *s, size_t length, unsigned percent);
+
+bool nulstr_contains(const char*nulstr, const char *needle);
+
+char* strshorten(char *s, size_t l);
+
+char *strreplace(const char *text, const char *old_string, const char *new_string);
+
+char *strip_tab_ansi(char **p, size_t *l);
+
+char *strextend(char **x, ...) _sentinel_;
+
+char *strrep(const char *s, unsigned n);
+
+int split_pair(const char *s, const char *sep, char **l, char **r);
+
+int free_and_strdup(char **p, const char *s);
+
+/* 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);
+}
+
+void* memory_erase(void *p, size_t l);
+char *string_erase(char *x);
+
+char *string_free_erase(char *s);
+DEFINE_TRIVIAL_CLEANUP_FUNC(char *, string_free_erase);
+#define _cleanup_string_free_erase_ _cleanup_(string_free_erasep)
+
+bool string_is_safe(const char *p) _pure_;
diff --git a/src/basic/strv.c b/src/basic/strv.c
index b9aef64b15..0a3d15706f 100644
--- a/src/basic/strv.c
+++ b/src/basic/strv.c
@@ -19,13 +19,19 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
+#include <errno.h>
+#include <fnmatch.h>
#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
-#include <errno.h>
-#include "util.h"
+#include "alloc-util.h"
+#include "escape.h"
+#include "extract-word.h"
+#include "string-util.h"
#include "strv.h"
+#include "util.h"
char *strv_find(char **l, const char *name) {
char **i;
@@ -86,6 +92,15 @@ char **strv_free(char **l) {
return NULL;
}
+char **strv_free_erase(char **l) {
+ char **i;
+
+ STRV_FOREACH(i, l)
+ string_erase(*i);
+
+ return strv_free(l);
+}
+
char **strv_copy(char * const *l) {
char **r, **k;
@@ -188,17 +203,48 @@ char **strv_new(const char *x, ...) {
return r;
}
-int strv_extend_strv(char ***a, char **b) {
- int r;
- char **s;
+int strv_extend_strv(char ***a, char **b, bool filter_duplicates) {
+ char **s, **t;
+ size_t p, q, i = 0, j;
+
+ assert(a);
+
+ if (strv_isempty(b))
+ return 0;
+
+ p = strv_length(*a);
+ q = strv_length(b);
+
+ t = realloc(*a, sizeof(char*) * (p + q + 1));
+ if (!t)
+ return -ENOMEM;
+
+ t[p] = NULL;
+ *a = t;
STRV_FOREACH(s, b) {
- r = strv_extend(a, *s);
- if (r < 0)
- return r;
+
+ if (filter_duplicates && strv_contains(t, *s))
+ continue;
+
+ t[p+i] = strdup(*s);
+ if (!t[p+i])
+ goto rollback;
+
+ i++;
+ t[p+i] = NULL;
}
- return 0;
+ assert(i <= q);
+
+ return (int) i;
+
+rollback:
+ for (j = 0; j < i; j++)
+ free(t[p + j]);
+
+ t[p] = NULL;
+ return -ENOMEM;
}
int strv_extend_strv_concat(char ***a, char **b, const char *suffix) {
@@ -277,8 +323,8 @@ char **strv_split_newlines(const char *s) {
}
int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags) {
- size_t n = 0, allocated = 0;
_cleanup_strv_free_ char **l = NULL;
+ size_t n = 0, allocated = 0;
int r;
assert(t);
@@ -302,13 +348,16 @@ int strv_split_extract(char ***t, const char *s, const char *separators, Extract
l[n] = NULL;
}
- if (!l)
+ if (!l) {
l = new0(char*, 1);
+ if (!l)
+ return -ENOMEM;
+ }
*t = l;
l = NULL;
- return 0;
+ return (int) n;
}
char *strv_join(char **l, const char *separator) {
@@ -615,6 +664,41 @@ char **strv_split_nulstr(const char *s) {
return r;
}
+int strv_make_nulstr(char **l, char **p, size_t *q) {
+ size_t n_allocated = 0, n = 0;
+ _cleanup_free_ char *m = NULL;
+ char **i;
+
+ assert(p);
+ assert(q);
+
+ STRV_FOREACH(i, l) {
+ size_t z;
+
+ z = strlen(*i);
+
+ if (!GREEDY_REALLOC(m, n_allocated, n + z + 1))
+ return -ENOMEM;
+
+ memcpy(m + n, *i, z + 1);
+ n += z + 1;
+ }
+
+ if (!m) {
+ m = new0(char, 1);
+ if (!m)
+ return -ENOMEM;
+ n = 0;
+ }
+
+ *p = m;
+ *q = n;
+
+ m = NULL;
+
+ return 0;
+}
+
bool strv_overlap(char **a, char **b) {
char **i;
@@ -641,8 +725,12 @@ char **strv_sort(char **l) {
}
bool strv_equal(char **a, char **b) {
- if (!a || !b)
- return a == b;
+
+ if (strv_isempty(a))
+ return strv_isempty(b);
+
+ if (strv_isempty(b))
+ return false;
for ( ; *a || *b; ++a, ++b)
if (!streq_ptr(*a, *b))
@@ -720,3 +808,66 @@ bool strv_fnmatch(char* const* patterns, const char *s, int flags) {
return false;
}
+
+char ***strv_free_free(char ***l) {
+ char ***i;
+
+ if (!l)
+ return NULL;
+
+ for (i = l; *i; i++)
+ strv_free(*i);
+
+ free(l);
+ return NULL;
+}
+
+char **strv_skip(char **l, size_t n) {
+
+ while (n > 0) {
+ if (strv_isempty(l))
+ return l;
+
+ l++, n--;
+ }
+
+ return l;
+}
+
+int strv_extend_n(char ***l, const char *value, size_t n) {
+ size_t i, j, k;
+ char **nl;
+
+ assert(l);
+
+ if (!value)
+ return 0;
+ if (n == 0)
+ return 0;
+
+ /* Adds the value value n times to l */
+
+ k = strv_length(*l);
+
+ nl = realloc(*l, sizeof(char*) * (k + n + 1));
+ if (!nl)
+ return -ENOMEM;
+
+ *l = nl;
+
+ for (i = k; i < k + n; i++) {
+ nl[i] = strdup(value);
+ if (!nl[i])
+ goto rollback;
+ }
+
+ nl[i] = NULL;
+ return 0;
+
+rollback:
+ for (j = k; j < i; j++)
+ free(nl[j]);
+
+ nl[k] = NULL;
+ return -ENOMEM;
+}
diff --git a/src/basic/strv.h b/src/basic/strv.h
index f07da8cdf3..bb61db2638 100644
--- a/src/basic/strv.h
+++ b/src/basic/strv.h
@@ -21,10 +21,14 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <fnmatch.h>
#include <stdarg.h>
#include <stdbool.h>
-#include <fnmatch.h>
+#include <stddef.h>
+#include "alloc-util.h"
+#include "extract-word.h"
+#include "macro.h"
#include "util.h"
char *strv_find(char **l, const char *name) _pure_;
@@ -35,12 +39,16 @@ char **strv_free(char **l);
DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free);
#define _cleanup_strv_free_ _cleanup_(strv_freep)
+char **strv_free_erase(char **l);
+DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free_erase);
+#define _cleanup_strv_free_erase_ _cleanup_(strv_free_erasep)
+
void strv_clear(char **l);
char **strv_copy(char * const *l);
unsigned strv_length(char * const *l) _pure_;
-int strv_extend_strv(char ***a, char **b);
+int strv_extend_strv(char ***a, char **b, bool filter_duplicates);
int strv_extend_strv_concat(char ***a, char **b, const char *suffix);
int strv_extend(char ***l, const char *value);
int strv_extendf(char ***l, const char *format, ...) _printf_(2,0);
@@ -80,6 +88,7 @@ char *strv_join_quoted(char **l);
char **strv_parse_nulstr(const char *s, size_t l);
char **strv_split_nulstr(const char *s);
+int strv_make_nulstr(char **l, char **p, size_t *n);
bool strv_overlap(char **a, char **b) _pure_;
@@ -154,3 +163,9 @@ static inline bool strv_fnmatch_or_empty(char* const* patterns, const char *s, i
return strv_isempty(patterns) ||
strv_fnmatch(patterns, s, flags);
}
+
+char ***strv_free_free(char ***l);
+
+char **strv_skip(char **l, size_t n);
+
+int strv_extend_n(char ***l, const char *value, size_t n);
diff --git a/src/basic/strxcpyx.c b/src/basic/strxcpyx.c
index 6542c0abf5..c454171de1 100644
--- a/src/basic/strxcpyx.c
+++ b/src/basic/strxcpyx.c
@@ -25,8 +25,10 @@
* Returns the * remaining size, and 0 if the string was truncated.
*/
+#include <stdarg.h>
#include <stdio.h>
#include <string.h>
+
#include "strxcpyx.h"
size_t strpcpy(char **dest, size_t size, const char *src) {
diff --git a/src/basic/strxcpyx.h b/src/basic/strxcpyx.h
index ccc7e52f37..02e22e6294 100644
--- a/src/basic/strxcpyx.h
+++ b/src/basic/strxcpyx.h
@@ -22,6 +22,8 @@
***/
+#include <stddef.h>
+
#include "macro.h"
size_t strpcpy(char **dest, size_t size, const char *src);
diff --git a/src/basic/syslog-util.c b/src/basic/syslog-util.c
new file mode 100644
index 0000000000..7dc2761b6f
--- /dev/null
+++ b/src/basic/syslog-util.c
@@ -0,0 +1,116 @@
+/*-*- 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 <string.h>
+#include <syslog.h>
+
+#include "hexdecoct.h"
+#include "macro.h"
+#include "string-table.h"
+#include "syslog-util.h"
+
+int syslog_parse_priority(const char **p, int *priority, bool with_facility) {
+ int a = 0, b = 0, c = 0;
+ int k;
+
+ assert(p);
+ assert(*p);
+ assert(priority);
+
+ if ((*p)[0] != '<')
+ return 0;
+
+ if (!strchr(*p, '>'))
+ return 0;
+
+ if ((*p)[2] == '>') {
+ c = undecchar((*p)[1]);
+ k = 3;
+ } else if ((*p)[3] == '>') {
+ b = undecchar((*p)[1]);
+ c = undecchar((*p)[2]);
+ k = 4;
+ } else if ((*p)[4] == '>') {
+ a = undecchar((*p)[1]);
+ b = undecchar((*p)[2]);
+ c = undecchar((*p)[3]);
+ k = 5;
+ } else
+ return 0;
+
+ if (a < 0 || b < 0 || c < 0 ||
+ (!with_facility && (a || b || c > 7)))
+ return 0;
+
+ if (with_facility)
+ *priority = a*100 + b*10 + c;
+ else
+ *priority = (*priority & LOG_FACMASK) | c;
+
+ *p += k;
+ return 1;
+}
+
+static const char *const log_facility_unshifted_table[LOG_NFACILITIES] = {
+ [LOG_FAC(LOG_KERN)] = "kern",
+ [LOG_FAC(LOG_USER)] = "user",
+ [LOG_FAC(LOG_MAIL)] = "mail",
+ [LOG_FAC(LOG_DAEMON)] = "daemon",
+ [LOG_FAC(LOG_AUTH)] = "auth",
+ [LOG_FAC(LOG_SYSLOG)] = "syslog",
+ [LOG_FAC(LOG_LPR)] = "lpr",
+ [LOG_FAC(LOG_NEWS)] = "news",
+ [LOG_FAC(LOG_UUCP)] = "uucp",
+ [LOG_FAC(LOG_CRON)] = "cron",
+ [LOG_FAC(LOG_AUTHPRIV)] = "authpriv",
+ [LOG_FAC(LOG_FTP)] = "ftp",
+ [LOG_FAC(LOG_LOCAL0)] = "local0",
+ [LOG_FAC(LOG_LOCAL1)] = "local1",
+ [LOG_FAC(LOG_LOCAL2)] = "local2",
+ [LOG_FAC(LOG_LOCAL3)] = "local3",
+ [LOG_FAC(LOG_LOCAL4)] = "local4",
+ [LOG_FAC(LOG_LOCAL5)] = "local5",
+ [LOG_FAC(LOG_LOCAL6)] = "local6",
+ [LOG_FAC(LOG_LOCAL7)] = "local7"
+};
+
+DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(log_facility_unshifted, int, LOG_FAC(~0));
+
+bool log_facility_unshifted_is_valid(int facility) {
+ return facility >= 0 && facility <= LOG_FAC(~0);
+}
+
+static const char *const log_level_table[] = {
+ [LOG_EMERG] = "emerg",
+ [LOG_ALERT] = "alert",
+ [LOG_CRIT] = "crit",
+ [LOG_ERR] = "err",
+ [LOG_WARNING] = "warning",
+ [LOG_NOTICE] = "notice",
+ [LOG_INFO] = "info",
+ [LOG_DEBUG] = "debug"
+};
+
+DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(log_level, int, LOG_DEBUG);
+
+bool log_level_is_valid(int level) {
+ return level >= 0 && level <= LOG_DEBUG;
+}
diff --git a/src/basic/syslog-util.h b/src/basic/syslog-util.h
new file mode 100644
index 0000000000..eb79c6dbd8
--- /dev/null
+++ b/src/basic/syslog-util.h
@@ -0,0 +1,34 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+
+int log_facility_unshifted_to_string_alloc(int i, char **s);
+int log_facility_unshifted_from_string(const char *s);
+bool log_facility_unshifted_is_valid(int faciliy);
+
+int log_level_to_string_alloc(int i, char **s);
+int log_level_from_string(const char *s);
+bool log_level_is_valid(int level);
+
+int syslog_parse_priority(const char **p, int *priority, bool with_facility);
diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c
index dd3d525854..fedfc8a5df 100644
--- a/src/basic/terminal-util.c
+++ b/src/basic/terminal-util.c
@@ -17,26 +17,42 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/inotify.h>
+#include <sys/socket.h>
+#include <sys/sysmacros.h>
+#include <sys/time.h>
+#include <linux/kd.h>
+#include <linux/tiocl.h>
+#include <linux/vt.h>
+#include <poll.h>
+#include <signal.h>
#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 "alloc-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "io-util.h"
+#include "log.h"
+#include "macro.h"
+#include "parse-util.h"
+#include "process-util.h"
+#include "socket-util.h"
+#include "stat-util.h"
+#include "string-util.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;
@@ -48,7 +64,7 @@ int chvt(int vt) {
if (fd < 0)
return -errno;
- if (vt < 0) {
+ if (vt <= 0) {
int tiocl[2] = {
TIOCL_GETKMSGREDIRECT,
0
@@ -112,7 +128,7 @@ int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) {
errno = 0;
if (!fgets(line, sizeof(line), f))
- return errno ? -errno : -EIO;
+ return errno > 0 ? -errno : -EIO;
truncate_nl(line);
@@ -196,7 +212,7 @@ int ask_string(char **ret, const char *text, ...) {
errno = 0;
if (!fgets(line, sizeof(line), stdin))
- return errno ? -errno : -EIO;
+ return errno > 0 ? -errno : -EIO;
if (!endswith(line, "\n"))
putchar('\n');
@@ -412,7 +428,7 @@ int acquire_terminal(
assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0);
- /* Sometimes it makes sense to ignore TIOCSCTTY
+ /* 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)
@@ -480,10 +496,6 @@ int acquire_terminal(
safe_close(notify);
- r = reset_terminal_fd(fd, true);
- if (r < 0)
- log_warning_errno(r, "Failed to reset terminal: %m");
-
return fd;
fail:
@@ -539,8 +551,9 @@ int terminal_vhangup(const char *name) {
}
int vt_disallocate(const char *name) {
- int fd, r;
+ _cleanup_close_ int fd = -1;
unsigned u;
+ int r;
/* Deallocate the VT if possible. If not possible
* (i.e. because it is the active one), at least clear it
@@ -562,8 +575,6 @@ int vt_disallocate(const char *name) {
"\033[H" /* move home */
"\033[2J", /* clear screen */
10, false);
- safe_close(fd);
-
return 0;
}
@@ -583,7 +594,7 @@ int vt_disallocate(const char *name) {
return fd;
r = ioctl(fd, VT_DISALLOCATE, u);
- safe_close(fd);
+ fd = safe_close(fd);
if (r >= 0)
return 0;
@@ -602,32 +613,9 @@ int vt_disallocate(const char *name) {
"\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... */
-
- (void) ioctl(fd, KIOCSOUND, (int)(1193180/440));
- usleep(125*USEC_PER_MSEC);
-
- (void) ioctl(fd, KIOCSOUND, (int)(1193180/220));
- usleep(125*USEC_PER_MSEC);
-
- (void) ioctl(fd, KIOCSOUND, (int)(1193180/220));
- usleep(125*USEC_PER_MSEC);
-
- (void) ioctl(fd, KIOCSOUND, 0);
-}
-
int make_console_stdio(void) {
int fd, r;
@@ -637,6 +625,10 @@ int make_console_stdio(void) {
if (fd < 0)
return log_error_errno(fd, "Failed to acquire terminal: %m");
+ r = reset_terminal_fd(fd, true);
+ if (r < 0)
+ log_warning_errno(r, "Failed to reset terminal, ignoring: %m");
+
r = make_stdio(fd);
if (r < 0)
return log_error_errno(r, "Failed to duplicate terminal fd: %m");
@@ -644,84 +636,6 @@ int make_console_stdio(void) {
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);
@@ -1075,6 +989,33 @@ int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
return 0;
}
+int ptsname_malloc(int fd, char **ret) {
+ size_t l = 100;
+
+ assert(fd >= 0);
+ assert(ret);
+
+ for (;;) {
+ char *c;
+
+ c = new(char, l);
+ if (!c)
+ return -ENOMEM;
+
+ if (ptsname_r(fd, c, l) == 0) {
+ *ret = c;
+ return 0;
+ }
+ if (errno != ERANGE) {
+ free(c);
+ return -errno;
+ }
+
+ free(c);
+ l *= 2;
+ }
+}
+
int ptsname_namespace(int pty, char **ret) {
int no = -1, r;
@@ -1093,3 +1034,117 @@ int ptsname_namespace(int pty, char **ret) {
return 0;
}
+
+int openpt_in_namespace(pid_t pid, int flags) {
+ _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
+ _cleanup_close_pair_ int pair[2] = { -1, -1 };
+ siginfo_t si;
+ pid_t child;
+ int r;
+
+ assert(pid > 0);
+
+ r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd);
+ if (r < 0)
+ return r;
+
+ if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
+ return -errno;
+
+ child = fork();
+ if (child < 0)
+ return -errno;
+
+ if (child == 0) {
+ int master;
+
+ pair[0] = safe_close(pair[0]);
+
+ r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd);
+ if (r < 0)
+ _exit(EXIT_FAILURE);
+
+ master = posix_openpt(flags|O_NOCTTY|O_CLOEXEC);
+ if (master < 0)
+ _exit(EXIT_FAILURE);
+
+ if (unlockpt(master) < 0)
+ _exit(EXIT_FAILURE);
+
+ if (send_one_fd(pair[1], master, 0) < 0)
+ _exit(EXIT_FAILURE);
+
+ _exit(EXIT_SUCCESS);
+ }
+
+ pair[1] = safe_close(pair[1]);
+
+ r = wait_for_terminate(child, &si);
+ if (r < 0)
+ return r;
+ if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
+ return -EIO;
+
+ return receive_one_fd(pair[0], 0);
+}
+
+int open_terminal_in_namespace(pid_t pid, const char *name, int mode) {
+ _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
+ _cleanup_close_pair_ int pair[2] = { -1, -1 };
+ siginfo_t si;
+ pid_t child;
+ int r;
+
+ r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd);
+ if (r < 0)
+ return r;
+
+ if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
+ return -errno;
+
+ child = fork();
+ if (child < 0)
+ return -errno;
+
+ if (child == 0) {
+ int master;
+
+ pair[0] = safe_close(pair[0]);
+
+ r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd);
+ if (r < 0)
+ _exit(EXIT_FAILURE);
+
+ master = open_terminal(name, mode|O_NOCTTY|O_CLOEXEC);
+ if (master < 0)
+ _exit(EXIT_FAILURE);
+
+ if (send_one_fd(pair[1], master, 0) < 0)
+ _exit(EXIT_FAILURE);
+
+ _exit(EXIT_SUCCESS);
+ }
+
+ pair[1] = safe_close(pair[1]);
+
+ r = wait_for_terminate(child, &si);
+ if (r < 0)
+ return r;
+ if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
+ return -EIO;
+
+ return receive_one_fd(pair[0], 0);
+}
+
+bool colors_enabled(void) {
+ const char *colors;
+
+ colors = getenv("SYSTEMD_COLORS");
+ if (!colors) {
+ if (streq_ptr(getenv("TERM"), "dumb"))
+ return false;
+ return on_tty();
+ }
+
+ return parse_boolean(colors) != 0;
+}
diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h
index a9e325ccb3..a7c96a77cb 100644
--- a/src/basic/terminal-util.h
+++ b/src/basic/terminal-util.h
@@ -19,9 +19,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdbool.h>
#include <stdarg.h>
+#include <stdbool.h>
#include <stdio.h>
+#include <sys/types.h>
#include "macro.h"
#include "time-util.h"
@@ -67,15 +68,10 @@ 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);
@@ -83,37 +79,38 @@ unsigned lines(void);
void columns_lines_cache_reset(int _unused_ signum);
bool on_tty(void);
+bool colors_enabled(void);
static inline const char *ansi_underline(void) {
- return on_tty() ? ANSI_UNDERLINE : "";
+ return colors_enabled() ? ANSI_UNDERLINE : "";
}
static inline const char *ansi_highlight(void) {
- return on_tty() ? ANSI_HIGHLIGHT : "";
+ return colors_enabled() ? ANSI_HIGHLIGHT : "";
}
static inline const char *ansi_highlight_underline(void) {
- return on_tty() ? ANSI_HIGHLIGHT_UNDERLINE : "";
+ return colors_enabled() ? ANSI_HIGHLIGHT_UNDERLINE : "";
}
static inline const char *ansi_highlight_red(void) {
- return on_tty() ? ANSI_HIGHLIGHT_RED : "";
+ return colors_enabled() ? ANSI_HIGHLIGHT_RED : "";
}
static inline const char *ansi_highlight_green(void) {
- return on_tty() ? ANSI_HIGHLIGHT_GREEN : "";
+ return colors_enabled() ? ANSI_HIGHLIGHT_GREEN : "";
}
static inline const char *ansi_highlight_yellow(void) {
- return on_tty() ? ANSI_HIGHLIGHT_YELLOW : "";
+ return colors_enabled() ? ANSI_HIGHLIGHT_YELLOW : "";
}
static inline const char *ansi_highlight_blue(void) {
- return on_tty() ? ANSI_HIGHLIGHT_BLUE : "";
+ return colors_enabled() ? ANSI_HIGHLIGHT_BLUE : "";
}
static inline const char *ansi_normal(void) {
- return on_tty() ? ANSI_NORMAL : "";
+ return colors_enabled() ? ANSI_NORMAL : "";
}
int get_ctty_devnr(pid_t pid, dev_t *d);
@@ -122,4 +119,8 @@ int get_ctty(pid_t, dev_t *_devnr, char **r);
int getttyname_malloc(int fd, char **r);
int getttyname_harder(int fd, char **r);
+int ptsname_malloc(int fd, char **ret);
int ptsname_namespace(int pty, char **ret);
+
+int openpt_in_namespace(pid_t pid, int flags);
+int open_terminal_in_namespace(pid_t pid, const char *name, int mode);
diff --git a/src/basic/time-util.c b/src/basic/time-util.c
index 531931f6e1..bfc7cf870c 100644
--- a/src/basic/time-util.c
+++ b/src/basic/time-util.c
@@ -19,15 +19,28 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <time.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
#include <string.h>
-#include <sys/timex.h>
+#include <sys/stat.h>
+#include <sys/time.h>
#include <sys/timerfd.h>
-
-#include "util.h"
-#include "time-util.h"
+#include <sys/timex.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "log.h"
+#include "macro.h"
+#include "parse-util.h"
#include "path-util.h"
+#include "string-util.h"
#include "strv.h"
+#include "time-util.h"
usec_t now(clockid_t clock_id) {
struct timespec ts;
@@ -205,11 +218,8 @@ static char *format_timestamp_internal(char *buf, size_t l, usec_t t, bool utc)
return NULL;
sec = (time_t) (t / USEC_PER_SEC);
+ localtime_or_gmtime_r(&sec, &tm, utc);
- if (utc)
- gmtime_r(&sec, &tm);
- else
- localtime_r(&sec, &tm);
if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S %Z", &tm) <= 0)
return NULL;
@@ -235,10 +245,7 @@ static char *format_timestamp_internal_us(char *buf, size_t l, usec_t t, bool ut
return NULL;
sec = (time_t) (t / USEC_PER_SEC);
- if (utc)
- gmtime_r(&sec, &tm);
- else
- localtime_r(&sec, &tm);
+ localtime_or_gmtime_r(&sec, &tm, utc);
if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S", &tm) <= 0)
return NULL;
@@ -325,15 +332,15 @@ char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
const char *suffix;
usec_t usec;
} table[] = {
- { "y", USEC_PER_YEAR },
- { "month", USEC_PER_MONTH },
- { "w", USEC_PER_WEEK },
- { "d", USEC_PER_DAY },
- { "h", USEC_PER_HOUR },
- { "min", USEC_PER_MINUTE },
- { "s", USEC_PER_SEC },
- { "ms", USEC_PER_MSEC },
- { "us", 1 },
+ { "y", USEC_PER_YEAR },
+ { "month", USEC_PER_MONTH },
+ { "w", USEC_PER_WEEK },
+ { "d", USEC_PER_DAY },
+ { "h", USEC_PER_HOUR },
+ { "min", USEC_PER_MINUTE },
+ { "s", USEC_PER_SEC },
+ { "ms", USEC_PER_MSEC },
+ { "us", 1 },
};
unsigned i;
@@ -484,9 +491,10 @@ int parse_timestamp(const char *t, usec_t *usec) {
};
const char *k;
+ const char *utc;
struct tm tm, copy;
time_t x;
- usec_t plus = 0, minus = 0, ret;
+ usec_t x_usec, plus = 0, minus = 0, ret;
int r, weekday = -1;
unsigned i;
@@ -511,28 +519,15 @@ int parse_timestamp(const char *t, usec_t *usec) {
assert(t);
assert(usec);
- x = time(NULL);
- assert_se(localtime_r(&x, &tm));
- tm.tm_isdst = -1;
-
- if (streq(t, "now"))
- goto finish;
-
- else if (streq(t, "today")) {
- tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
- goto finish;
+ if (t[0] == '@')
+ return parse_sec(t + 1, usec);
- } else if (streq(t, "yesterday")) {
- tm.tm_mday --;
- tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
- goto finish;
+ ret = now(CLOCK_REALTIME);
- } else if (streq(t, "tomorrow")) {
- tm.tm_mday ++;
- tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+ if (streq(t, "now"))
goto finish;
- } else if (t[0] == '+') {
+ else if (t[0] == '+') {
r = parse_sec(t+1, &plus);
if (r < 0)
return r;
@@ -546,35 +541,51 @@ int parse_timestamp(const char *t, usec_t *usec) {
goto finish;
- } else if (t[0] == '@')
- return parse_sec(t + 1, usec);
-
- else if (endswith(t, " ago")) {
- _cleanup_free_ char *z;
+ } else if ((k = endswith(t, " ago"))) {
+ t = strndupa(t, k - t);
- z = strndup(t, strlen(t) - 4);
- if (!z)
- return -ENOMEM;
-
- r = parse_sec(z, &minus);
+ r = parse_sec(t, &minus);
if (r < 0)
return r;
goto finish;
- } else if (endswith(t, " left")) {
- _cleanup_free_ char *z;
- z = strndup(t, strlen(t) - 4);
- if (!z)
- return -ENOMEM;
+ } else if ((k = endswith(t, " left"))) {
+ t = strndupa(t, k - t);
- r = parse_sec(z, &plus);
+ r = parse_sec(t, &plus);
if (r < 0)
return r;
goto finish;
}
+ utc = endswith_no_case(t, " UTC");
+ if (utc)
+ t = strndupa(t, utc - t);
+
+ x = ret / USEC_PER_SEC;
+ x_usec = 0;
+
+ assert_se(localtime_or_gmtime_r(&x, &tm, utc));
+ tm.tm_isdst = -1;
+
+ if (streq(t, "today")) {
+ tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+ goto from_tm;
+
+ } else if (streq(t, "yesterday")) {
+ tm.tm_mday --;
+ tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+ goto from_tm;
+
+ } else if (streq(t, "tomorrow")) {
+ tm.tm_mday ++;
+ tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+ goto from_tm;
+ }
+
+
for (i = 0; i < ELEMENTSOF(day_nr); i++) {
size_t skip;
@@ -592,66 +603,95 @@ int parse_timestamp(const char *t, usec_t *usec) {
copy = tm;
k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
- if (k && *k == 0)
- goto finish;
+ if (k) {
+ if (*k == '.')
+ goto parse_usec;
+ else if (*k == 0)
+ goto from_tm;
+ }
tm = copy;
k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
- if (k && *k == 0)
- goto finish;
+ if (k) {
+ if (*k == '.')
+ goto parse_usec;
+ else if (*k == 0)
+ goto from_tm;
+ }
tm = copy;
k = strptime(t, "%y-%m-%d %H:%M", &tm);
if (k && *k == 0) {
tm.tm_sec = 0;
- goto finish;
+ goto from_tm;
}
tm = copy;
k = strptime(t, "%Y-%m-%d %H:%M", &tm);
if (k && *k == 0) {
tm.tm_sec = 0;
- goto finish;
+ goto from_tm;
}
tm = copy;
k = strptime(t, "%y-%m-%d", &tm);
if (k && *k == 0) {
tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
- goto finish;
+ goto from_tm;
}
tm = copy;
k = strptime(t, "%Y-%m-%d", &tm);
if (k && *k == 0) {
tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
- goto finish;
+ goto from_tm;
}
tm = copy;
k = strptime(t, "%H:%M:%S", &tm);
- if (k && *k == 0)
- goto finish;
+ if (k) {
+ if (*k == '.')
+ goto parse_usec;
+ else if (*k == 0)
+ goto from_tm;
+ }
tm = copy;
k = strptime(t, "%H:%M", &tm);
if (k && *k == 0) {
tm.tm_sec = 0;
- goto finish;
+ goto from_tm;
}
return -EINVAL;
-finish:
- x = mktime(&tm);
+parse_usec:
+ {
+ unsigned add;
+
+ k++;
+ r = parse_fractional_part_u(&k, 6, &add);
+ if (r < 0)
+ return -EINVAL;
+
+ if (*k)
+ return -EINVAL;
+
+ x_usec = add;
+
+ }
+
+from_tm:
+ x = mktime_or_timegm(&tm, utc);
if (x == (time_t) -1)
return -EINVAL;
if (weekday >= 0 && tm.tm_wday != weekday)
return -EINVAL;
- ret = (usec_t) x * USEC_PER_SEC;
+ ret = (usec_t) x * USEC_PER_SEC + x_usec;
+finish:
ret += plus;
if (ret > minus)
ret -= minus;
@@ -663,39 +703,40 @@ finish:
return 0;
}
-int parse_sec(const char *t, usec_t *usec) {
+int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
+
static const struct {
const char *suffix;
usec_t usec;
} table[] = {
- { "seconds", USEC_PER_SEC },
- { "second", USEC_PER_SEC },
- { "sec", USEC_PER_SEC },
- { "s", USEC_PER_SEC },
+ { "seconds", USEC_PER_SEC },
+ { "second", USEC_PER_SEC },
+ { "sec", USEC_PER_SEC },
+ { "s", USEC_PER_SEC },
{ "minutes", USEC_PER_MINUTE },
- { "minute", USEC_PER_MINUTE },
- { "min", USEC_PER_MINUTE },
- { "months", USEC_PER_MONTH },
- { "month", USEC_PER_MONTH },
- { "msec", USEC_PER_MSEC },
- { "ms", USEC_PER_MSEC },
- { "m", USEC_PER_MINUTE },
- { "hours", USEC_PER_HOUR },
- { "hour", USEC_PER_HOUR },
- { "hr", USEC_PER_HOUR },
- { "h", USEC_PER_HOUR },
- { "days", USEC_PER_DAY },
- { "day", USEC_PER_DAY },
- { "d", USEC_PER_DAY },
- { "weeks", USEC_PER_WEEK },
- { "week", USEC_PER_WEEK },
- { "w", USEC_PER_WEEK },
- { "years", USEC_PER_YEAR },
- { "year", USEC_PER_YEAR },
- { "y", USEC_PER_YEAR },
- { "usec", 1ULL },
- { "us", 1ULL },
- { "", USEC_PER_SEC }, /* default is sec */
+ { "minute", USEC_PER_MINUTE },
+ { "min", USEC_PER_MINUTE },
+ { "months", USEC_PER_MONTH },
+ { "month", USEC_PER_MONTH },
+ { "M", USEC_PER_MONTH },
+ { "msec", USEC_PER_MSEC },
+ { "ms", USEC_PER_MSEC },
+ { "m", USEC_PER_MINUTE },
+ { "hours", USEC_PER_HOUR },
+ { "hour", USEC_PER_HOUR },
+ { "hr", USEC_PER_HOUR },
+ { "h", USEC_PER_HOUR },
+ { "days", USEC_PER_DAY },
+ { "day", USEC_PER_DAY },
+ { "d", USEC_PER_DAY },
+ { "weeks", USEC_PER_WEEK },
+ { "week", USEC_PER_WEEK },
+ { "w", USEC_PER_WEEK },
+ { "years", USEC_PER_YEAR },
+ { "year", USEC_PER_YEAR },
+ { "y", USEC_PER_YEAR },
+ { "usec", 1ULL },
+ { "us", 1ULL },
};
const char *p, *s;
@@ -704,6 +745,7 @@ int parse_sec(const char *t, usec_t *usec) {
assert(t);
assert(usec);
+ assert(default_unit > 0);
p = t;
@@ -722,6 +764,7 @@ int parse_sec(const char *t, usec_t *usec) {
long long l, z = 0;
char *e;
unsigned i, n = 0;
+ usec_t multiplier, k;
p += strspn(p, WHITESPACE);
@@ -764,21 +807,24 @@ int parse_sec(const char *t, usec_t *usec) {
for (i = 0; i < ELEMENTSOF(table); i++)
if (startswith(e, table[i].suffix)) {
- usec_t k = (usec_t) z * table[i].usec;
-
- for (; n > 0; n--)
- k /= 10;
-
- r += (usec_t) l * table[i].usec + k;
+ multiplier = table[i].usec;
p = e + strlen(table[i].suffix);
-
- something = true;
break;
}
- if (i >= ELEMENTSOF(table))
- return -EINVAL;
+ if (i >= ELEMENTSOF(table)) {
+ multiplier = default_unit;
+ p = e;
+ }
+ something = true;
+
+ k = (usec_t) z * multiplier;
+
+ for (; n > 0; n--)
+ k /= 10;
+
+ r += (usec_t) l * multiplier + k;
}
*usec = r;
@@ -786,6 +832,10 @@ int parse_sec(const char *t, usec_t *usec) {
return 0;
}
+int parse_sec(const char *t, usec_t *usec) {
+ return parse_time(t, usec, USEC_PER_SEC);
+}
+
int parse_nsec(const char *t, nsec_t *nsec) {
static const struct {
const char *suffix;
@@ -1072,3 +1122,25 @@ int get_timezone(char **tz) {
*tz = z;
return 0;
}
+
+time_t mktime_or_timegm(struct tm *tm, bool utc) {
+ return utc ? timegm(tm) : mktime(tm);
+}
+
+struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc) {
+ return utc ? gmtime_r(t, tm) : localtime_r(t, tm);
+}
+
+unsigned long usec_to_jiffies(usec_t u) {
+ static thread_local unsigned long hz = 0;
+ long r;
+
+ if (hz == 0) {
+ r = sysconf(_SC_CLK_TCK);
+
+ assert(r > 0);
+ hz = (unsigned long) r;
+ }
+
+ return DIV_ROUND_UP(u , USEC_PER_SEC / hz);
+}
diff --git a/src/basic/time-util.h b/src/basic/time-util.h
index 1af01541fc..7321e3c670 100644
--- a/src/basic/time-util.h
+++ b/src/basic/time-util.h
@@ -21,8 +21,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
#include <inttypes.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <time.h>
typedef uint64_t usec_t;
typedef uint64_t nsec_t;
@@ -103,6 +107,7 @@ int dual_timestamp_deserialize(const char *value, dual_timestamp *t);
int parse_timestamp(const char *t, usec_t *usec);
int parse_sec(const char *t, usec_t *usec);
+int parse_time(const char *t, usec_t *usec, usec_t default_unit);
int parse_nsec(const char *t, nsec_t *nsec);
bool ntp_synced(void);
@@ -117,3 +122,8 @@ clockid_t clock_boottime_or_monotonic(void);
"xstrftime: " #buf "[] must be big enough")
int get_timezone(char **timezone);
+
+time_t mktime_or_timegm(struct tm *tm, bool utc);
+struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc);
+
+unsigned long usec_to_jiffies(usec_t usec);
diff --git a/src/basic/umask-util.h b/src/basic/umask-util.h
new file mode 100644
index 0000000000..8ed34658b4
--- /dev/null
+++ b/src/basic/umask-util.h
@@ -0,0 +1,48 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "macro.h"
+
+static inline void umaskp(mode_t *u) {
+ umask(*u);
+}
+
+#define _cleanup_umask_ _cleanup_(umaskp)
+
+struct _umask_struct_ {
+ mode_t mask;
+ bool quit;
+};
+
+static inline void _reset_umask_(struct _umask_struct_ *s) {
+ umask(s->mask);
+};
+
+#define RUN_WITH_UMASK(mask) \
+ for (_cleanup_(_reset_umask_) struct _umask_struct_ _saved_umask_ = { umask(mask), false }; \
+ !_saved_umask_.quit ; \
+ _saved_umask_.quit = true)
diff --git a/src/basic/unaligned.h b/src/basic/unaligned.h
index d6181dd9a9..a8115eaa1f 100644
--- a/src/basic/unaligned.h
+++ b/src/basic/unaligned.h
@@ -21,8 +21,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <endian.h>
#include <stdint.h>
+/* BE */
+
static inline uint16_t unaligned_read_be16(const void *_u) {
const uint8_t *u = _u;
@@ -64,3 +67,47 @@ static inline void unaligned_write_be64(void *_u, uint64_t a) {
unaligned_write_be32(u, (uint32_t) (a >> 32));
unaligned_write_be32(u + 4, (uint32_t) a);
}
+
+/* LE */
+
+static inline uint16_t unaligned_read_le16(const void *_u) {
+ const uint8_t *u = _u;
+
+ return (((uint16_t) u[1]) << 8) |
+ ((uint16_t) u[0]);
+}
+
+static inline uint32_t unaligned_read_le32(const void *_u) {
+ const uint8_t *u = _u;
+
+ return (((uint32_t) unaligned_read_le16(u + 2)) << 16) |
+ ((uint32_t) unaligned_read_le16(u));
+}
+
+static inline uint64_t unaligned_read_le64(const void *_u) {
+ const uint8_t *u = _u;
+
+ return (((uint64_t) unaligned_read_le32(u + 4)) << 32) |
+ ((uint64_t) unaligned_read_le32(u));
+}
+
+static inline void unaligned_write_le16(void *_u, uint16_t a) {
+ uint8_t *u = _u;
+
+ u[0] = (uint8_t) a;
+ u[1] = (uint8_t) (a >> 8);
+}
+
+static inline void unaligned_write_le32(void *_u, uint32_t a) {
+ uint8_t *u = _u;
+
+ unaligned_write_le16(u, (uint16_t) a);
+ unaligned_write_le16(u + 2, (uint16_t) (a >> 16));
+}
+
+static inline void unaligned_write_le64(void *_u, uint64_t a) {
+ uint8_t *u = _u;
+
+ unaligned_write_le32(u, (uint32_t) a);
+ unaligned_write_le32(u + 4, (uint32_t) (a >> 32));
+}
diff --git a/src/basic/unit-name.c b/src/basic/unit-name.c
index 8742ee757f..5fc3b9d6fd 100644
--- a/src/basic/unit-name.c
+++ b/src/basic/unit-name.c
@@ -20,14 +20,20 @@
***/
#include <errno.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
#include <string.h>
-#include "path-util.h"
+#include "alloc-util.h"
#include "bus-label.h"
-#include "util.h"
-#include "unit-name.h"
-#include "def.h"
+#include "hexdecoct.h"
+#include "macro.h"
+#include "path-util.h"
+#include "string-table.h"
+#include "string-util.h"
#include "strv.h"
+#include "unit-name.h"
#define VALID_CHARS \
DIGITS LETTERS \
@@ -593,7 +599,6 @@ const char* unit_dbus_interface_from_type(UnitType t) {
[UNIT_SOCKET] = "org.freedesktop.systemd1.Socket",
[UNIT_BUSNAME] = "org.freedesktop.systemd1.BusName",
[UNIT_TARGET] = "org.freedesktop.systemd1.Target",
- [UNIT_SNAPSHOT] = "org.freedesktop.systemd1.Snapshot",
[UNIT_DEVICE] = "org.freedesktop.systemd1.Device",
[UNIT_MOUNT] = "org.freedesktop.systemd1.Mount",
[UNIT_AUTOMOUNT] = "org.freedesktop.systemd1.Automount",
@@ -651,7 +656,7 @@ static char *do_escape_mangle(const char *f, UnitNameMangle allow_globs, char *t
* /blah/blah is converted to blah-blah.mount, anything else is left alone,
* except that @suffix is appended if a valid unit suffix is not present.
*
- * If @allow_globs, globs characters are preserved. Otherwise they are escaped.
+ * If @allow_globs, globs characters are preserved. Otherwise, they are escaped.
*/
int unit_name_mangle_with_suffix(const char *name, UnitNameMangle allow_globs, const char *suffix, char **ret) {
char *s, *t;
@@ -815,7 +820,6 @@ static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
[UNIT_SOCKET] = "socket",
[UNIT_BUSNAME] = "busname",
[UNIT_TARGET] = "target",
- [UNIT_SNAPSHOT] = "snapshot",
[UNIT_DEVICE] = "device",
[UNIT_MOUNT] = "mount",
[UNIT_AUTOMOUNT] = "automount",
@@ -839,18 +843,171 @@ static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
+static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = {
+ [UNIT_ACTIVE] = "active",
+ [UNIT_RELOADING] = "reloading",
+ [UNIT_INACTIVE] = "inactive",
+ [UNIT_FAILED] = "failed",
+ [UNIT_ACTIVATING] = "activating",
+ [UNIT_DEACTIVATING] = "deactivating"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(unit_active_state, UnitActiveState);
+
+static const char* const automount_state_table[_AUTOMOUNT_STATE_MAX] = {
+ [AUTOMOUNT_DEAD] = "dead",
+ [AUTOMOUNT_WAITING] = "waiting",
+ [AUTOMOUNT_RUNNING] = "running",
+ [AUTOMOUNT_FAILED] = "failed"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(automount_state, AutomountState);
+
+static const char* const busname_state_table[_BUSNAME_STATE_MAX] = {
+ [BUSNAME_DEAD] = "dead",
+ [BUSNAME_MAKING] = "making",
+ [BUSNAME_REGISTERED] = "registered",
+ [BUSNAME_LISTENING] = "listening",
+ [BUSNAME_RUNNING] = "running",
+ [BUSNAME_SIGTERM] = "sigterm",
+ [BUSNAME_SIGKILL] = "sigkill",
+ [BUSNAME_FAILED] = "failed",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(busname_state, BusNameState);
+
+static const char* const device_state_table[_DEVICE_STATE_MAX] = {
+ [DEVICE_DEAD] = "dead",
+ [DEVICE_TENTATIVE] = "tentative",
+ [DEVICE_PLUGGED] = "plugged",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(device_state, DeviceState);
+
+static const char* const mount_state_table[_MOUNT_STATE_MAX] = {
+ [MOUNT_DEAD] = "dead",
+ [MOUNT_MOUNTING] = "mounting",
+ [MOUNT_MOUNTING_DONE] = "mounting-done",
+ [MOUNT_MOUNTED] = "mounted",
+ [MOUNT_REMOUNTING] = "remounting",
+ [MOUNT_UNMOUNTING] = "unmounting",
+ [MOUNT_MOUNTING_SIGTERM] = "mounting-sigterm",
+ [MOUNT_MOUNTING_SIGKILL] = "mounting-sigkill",
+ [MOUNT_REMOUNTING_SIGTERM] = "remounting-sigterm",
+ [MOUNT_REMOUNTING_SIGKILL] = "remounting-sigkill",
+ [MOUNT_UNMOUNTING_SIGTERM] = "unmounting-sigterm",
+ [MOUNT_UNMOUNTING_SIGKILL] = "unmounting-sigkill",
+ [MOUNT_FAILED] = "failed"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(mount_state, MountState);
+
+static const char* const path_state_table[_PATH_STATE_MAX] = {
+ [PATH_DEAD] = "dead",
+ [PATH_WAITING] = "waiting",
+ [PATH_RUNNING] = "running",
+ [PATH_FAILED] = "failed"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(path_state, PathState);
+
+static const char* const scope_state_table[_SCOPE_STATE_MAX] = {
+ [SCOPE_DEAD] = "dead",
+ [SCOPE_RUNNING] = "running",
+ [SCOPE_ABANDONED] = "abandoned",
+ [SCOPE_STOP_SIGTERM] = "stop-sigterm",
+ [SCOPE_STOP_SIGKILL] = "stop-sigkill",
+ [SCOPE_FAILED] = "failed",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(scope_state, ScopeState);
+
+static const char* const service_state_table[_SERVICE_STATE_MAX] = {
+ [SERVICE_DEAD] = "dead",
+ [SERVICE_START_PRE] = "start-pre",
+ [SERVICE_START] = "start",
+ [SERVICE_START_POST] = "start-post",
+ [SERVICE_RUNNING] = "running",
+ [SERVICE_EXITED] = "exited",
+ [SERVICE_RELOAD] = "reload",
+ [SERVICE_STOP] = "stop",
+ [SERVICE_STOP_SIGABRT] = "stop-sigabrt",
+ [SERVICE_STOP_SIGTERM] = "stop-sigterm",
+ [SERVICE_STOP_SIGKILL] = "stop-sigkill",
+ [SERVICE_STOP_POST] = "stop-post",
+ [SERVICE_FINAL_SIGTERM] = "final-sigterm",
+ [SERVICE_FINAL_SIGKILL] = "final-sigkill",
+ [SERVICE_FAILED] = "failed",
+ [SERVICE_AUTO_RESTART] = "auto-restart",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(service_state, ServiceState);
+
+static const char* const slice_state_table[_SLICE_STATE_MAX] = {
+ [SLICE_DEAD] = "dead",
+ [SLICE_ACTIVE] = "active"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(slice_state, SliceState);
+
+static const char* const socket_state_table[_SOCKET_STATE_MAX] = {
+ [SOCKET_DEAD] = "dead",
+ [SOCKET_START_PRE] = "start-pre",
+ [SOCKET_START_CHOWN] = "start-chown",
+ [SOCKET_START_POST] = "start-post",
+ [SOCKET_LISTENING] = "listening",
+ [SOCKET_RUNNING] = "running",
+ [SOCKET_STOP_PRE] = "stop-pre",
+ [SOCKET_STOP_PRE_SIGTERM] = "stop-pre-sigterm",
+ [SOCKET_STOP_PRE_SIGKILL] = "stop-pre-sigkill",
+ [SOCKET_STOP_POST] = "stop-post",
+ [SOCKET_FINAL_SIGTERM] = "final-sigterm",
+ [SOCKET_FINAL_SIGKILL] = "final-sigkill",
+ [SOCKET_FAILED] = "failed"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(socket_state, SocketState);
+
+static const char* const swap_state_table[_SWAP_STATE_MAX] = {
+ [SWAP_DEAD] = "dead",
+ [SWAP_ACTIVATING] = "activating",
+ [SWAP_ACTIVATING_DONE] = "activating-done",
+ [SWAP_ACTIVE] = "active",
+ [SWAP_DEACTIVATING] = "deactivating",
+ [SWAP_ACTIVATING_SIGTERM] = "activating-sigterm",
+ [SWAP_ACTIVATING_SIGKILL] = "activating-sigkill",
+ [SWAP_DEACTIVATING_SIGTERM] = "deactivating-sigterm",
+ [SWAP_DEACTIVATING_SIGKILL] = "deactivating-sigkill",
+ [SWAP_FAILED] = "failed"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(swap_state, SwapState);
+
+static const char* const target_state_table[_TARGET_STATE_MAX] = {
+ [TARGET_DEAD] = "dead",
+ [TARGET_ACTIVE] = "active"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(target_state, TargetState);
+
+static const char* const timer_state_table[_TIMER_STATE_MAX] = {
+ [TIMER_DEAD] = "dead",
+ [TIMER_WAITING] = "waiting",
+ [TIMER_RUNNING] = "running",
+ [TIMER_ELAPSED] = "elapsed",
+ [TIMER_FAILED] = "failed"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState);
+
static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
[UNIT_REQUIRES] = "Requires",
- [UNIT_REQUIRES_OVERRIDABLE] = "RequiresOverridable",
[UNIT_REQUISITE] = "Requisite",
- [UNIT_REQUISITE_OVERRIDABLE] = "RequisiteOverridable",
[UNIT_WANTS] = "Wants",
[UNIT_BINDS_TO] = "BindsTo",
[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/basic/unit-name.h b/src/basic/unit-name.h
index 28b3a555f3..03c1a6e4ac 100644
--- a/src/basic/unit-name.h
+++ b/src/basic/unit-name.h
@@ -27,16 +27,11 @@
#define UNIT_NAME_MAX 256
-typedef enum UnitType UnitType;
-typedef enum UnitLoadState UnitLoadState;
-typedef enum UnitDependency UnitDependency;
-
-enum UnitType {
+typedef enum UnitType {
UNIT_SERVICE = 0,
UNIT_SOCKET,
UNIT_BUSNAME,
UNIT_TARGET,
- UNIT_SNAPSHOT,
UNIT_DEVICE,
UNIT_MOUNT,
UNIT_AUTOMOUNT,
@@ -47,9 +42,9 @@ enum UnitType {
UNIT_SCOPE,
_UNIT_TYPE_MAX,
_UNIT_TYPE_INVALID = -1
-};
+} UnitType;
-enum UnitLoadState {
+typedef enum UnitLoadState {
UNIT_STUB = 0,
UNIT_LOADED,
UNIT_NOT_FOUND,
@@ -58,23 +53,179 @@ enum UnitLoadState {
UNIT_MASKED,
_UNIT_LOAD_STATE_MAX,
_UNIT_LOAD_STATE_INVALID = -1
-};
-
-enum UnitDependency {
+} UnitLoadState;
+
+typedef enum UnitActiveState {
+ UNIT_ACTIVE,
+ UNIT_RELOADING,
+ UNIT_INACTIVE,
+ UNIT_FAILED,
+ UNIT_ACTIVATING,
+ UNIT_DEACTIVATING,
+ _UNIT_ACTIVE_STATE_MAX,
+ _UNIT_ACTIVE_STATE_INVALID = -1
+} UnitActiveState;
+
+typedef enum AutomountState {
+ AUTOMOUNT_DEAD,
+ AUTOMOUNT_WAITING,
+ AUTOMOUNT_RUNNING,
+ AUTOMOUNT_FAILED,
+ _AUTOMOUNT_STATE_MAX,
+ _AUTOMOUNT_STATE_INVALID = -1
+} AutomountState;
+
+typedef enum BusNameState {
+ BUSNAME_DEAD,
+ BUSNAME_MAKING,
+ BUSNAME_REGISTERED,
+ BUSNAME_LISTENING,
+ BUSNAME_RUNNING,
+ BUSNAME_SIGTERM,
+ BUSNAME_SIGKILL,
+ BUSNAME_FAILED,
+ _BUSNAME_STATE_MAX,
+ _BUSNAME_STATE_INVALID = -1
+} BusNameState;
+
+/* We simply watch devices, we cannot plug/unplug them. That
+ * simplifies the state engine greatly */
+typedef enum DeviceState {
+ DEVICE_DEAD,
+ 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 MountState {
+ MOUNT_DEAD,
+ 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,
+ MOUNT_MOUNTING_SIGTERM,
+ MOUNT_MOUNTING_SIGKILL,
+ MOUNT_REMOUNTING_SIGTERM,
+ MOUNT_REMOUNTING_SIGKILL,
+ MOUNT_UNMOUNTING_SIGTERM,
+ MOUNT_UNMOUNTING_SIGKILL,
+ MOUNT_FAILED,
+ _MOUNT_STATE_MAX,
+ _MOUNT_STATE_INVALID = -1
+} MountState;
+
+typedef enum PathState {
+ PATH_DEAD,
+ PATH_WAITING,
+ PATH_RUNNING,
+ PATH_FAILED,
+ _PATH_STATE_MAX,
+ _PATH_STATE_INVALID = -1
+} PathState;
+
+typedef enum ScopeState {
+ SCOPE_DEAD,
+ SCOPE_RUNNING,
+ SCOPE_ABANDONED,
+ SCOPE_STOP_SIGTERM,
+ SCOPE_STOP_SIGKILL,
+ SCOPE_FAILED,
+ _SCOPE_STATE_MAX,
+ _SCOPE_STATE_INVALID = -1
+} ScopeState;
+
+typedef enum ServiceState {
+ SERVICE_DEAD,
+ SERVICE_START_PRE,
+ SERVICE_START,
+ SERVICE_START_POST,
+ SERVICE_RUNNING,
+ SERVICE_EXITED, /* Nothing is running anymore, but RemainAfterExit is true hence this is OK */
+ SERVICE_RELOAD,
+ SERVICE_STOP, /* No STOP_PRE state, instead just register multiple STOP executables */
+ SERVICE_STOP_SIGABRT, /* Watchdog timeout */
+ SERVICE_STOP_SIGTERM,
+ SERVICE_STOP_SIGKILL,
+ SERVICE_STOP_POST,
+ SERVICE_FINAL_SIGTERM, /* In case the STOP_POST executable hangs, we shoot that down, too */
+ SERVICE_FINAL_SIGKILL,
+ SERVICE_FAILED,
+ SERVICE_AUTO_RESTART,
+ _SERVICE_STATE_MAX,
+ _SERVICE_STATE_INVALID = -1
+} ServiceState;
+
+typedef enum SliceState {
+ SLICE_DEAD,
+ SLICE_ACTIVE,
+ _SLICE_STATE_MAX,
+ _SLICE_STATE_INVALID = -1
+} SliceState;
+
+typedef enum SocketState {
+ SOCKET_DEAD,
+ SOCKET_START_PRE,
+ SOCKET_START_CHOWN,
+ SOCKET_START_POST,
+ SOCKET_LISTENING,
+ SOCKET_RUNNING,
+ SOCKET_STOP_PRE,
+ SOCKET_STOP_PRE_SIGTERM,
+ SOCKET_STOP_PRE_SIGKILL,
+ SOCKET_STOP_POST,
+ SOCKET_FINAL_SIGTERM,
+ SOCKET_FINAL_SIGKILL,
+ SOCKET_FAILED,
+ _SOCKET_STATE_MAX,
+ _SOCKET_STATE_INVALID = -1
+} SocketState;
+
+typedef enum SwapState {
+ SWAP_DEAD,
+ SWAP_ACTIVATING, /* /sbin/swapon is running, but the swap not yet enabled. */
+ SWAP_ACTIVATING_DONE, /* /sbin/swapon is running, and the swap is done. */
+ SWAP_ACTIVE,
+ SWAP_DEACTIVATING,
+ SWAP_ACTIVATING_SIGTERM,
+ SWAP_ACTIVATING_SIGKILL,
+ SWAP_DEACTIVATING_SIGTERM,
+ SWAP_DEACTIVATING_SIGKILL,
+ SWAP_FAILED,
+ _SWAP_STATE_MAX,
+ _SWAP_STATE_INVALID = -1
+} SwapState;
+
+
+typedef enum TargetState {
+ TARGET_DEAD,
+ TARGET_ACTIVE,
+ _TARGET_STATE_MAX,
+ _TARGET_STATE_INVALID = -1
+} TargetState;
+
+typedef enum TimerState {
+ TIMER_DEAD,
+ TIMER_WAITING,
+ TIMER_RUNNING,
+ TIMER_ELAPSED,
+ TIMER_FAILED,
+ _TIMER_STATE_MAX,
+ _TIMER_STATE_INVALID = -1
+} TimerState;
+
+typedef enum UnitDependency {
/* Positive dependencies */
UNIT_REQUIRES,
- UNIT_REQUIRES_OVERRIDABLE,
UNIT_REQUISITE,
- UNIT_REQUISITE_OVERRIDABLE,
UNIT_WANTS,
UNIT_BINDS_TO,
UNIT_PART_OF,
/* Inverse of the above */
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,7 +258,7 @@ enum UnitDependency {
_UNIT_DEPENDENCY_MAX,
_UNIT_DEPENDENCY_INVALID = -1
-};
+} UnitDependency;
typedef enum UnitNameFlags {
UNIT_NAME_PLAIN = 1, /* Allow foo.service */
@@ -176,5 +327,44 @@ 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_active_state_to_string(UnitActiveState i) _const_;
+UnitActiveState unit_active_state_from_string(const char *s) _pure_;
+
+const char* automount_state_to_string(AutomountState i) _const_;
+AutomountState automount_state_from_string(const char *s) _pure_;
+
+const char* busname_state_to_string(BusNameState i) _const_;
+BusNameState busname_state_from_string(const char *s) _pure_;
+
+const char* device_state_to_string(DeviceState i) _const_;
+DeviceState device_state_from_string(const char *s) _pure_;
+
+const char* mount_state_to_string(MountState i) _const_;
+MountState mount_state_from_string(const char *s) _pure_;
+
+const char* path_state_to_string(PathState i) _const_;
+PathState path_state_from_string(const char *s) _pure_;
+
+const char* scope_state_to_string(ScopeState i) _const_;
+ScopeState scope_state_from_string(const char *s) _pure_;
+
+const char* service_state_to_string(ServiceState i) _const_;
+ServiceState service_state_from_string(const char *s) _pure_;
+
+const char* slice_state_to_string(SliceState i) _const_;
+SliceState slice_state_from_string(const char *s) _pure_;
+
+const char* socket_state_to_string(SocketState i) _const_;
+SocketState socket_state_from_string(const char *s) _pure_;
+
+const char* swap_state_to_string(SwapState i) _const_;
+SwapState swap_state_from_string(const char *s) _pure_;
+
+const char* target_state_to_string(TargetState i) _const_;
+TargetState target_state_from_string(const char *s) _pure_;
+
+const char *timer_state_to_string(TimerState i) _const_;
+TimerState timer_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/basic/user-util.c b/src/basic/user-util.c
new file mode 100644
index 0000000000..70a6e1f5e4
--- /dev/null
+++ b/src/basic/user-util.c
@@ -0,0 +1,482 @@
+/*-*- 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 <alloca.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "formats-util.h"
+#include "macro.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "string-util.h"
+#include "user-util.h"
+
+bool uid_is_valid(uid_t uid) {
+
+ /* Some libc APIs use UID_INVALID as special placeholder */
+ if (uid == (uid_t) UINT32_C(0xFFFFFFFF))
+ return false;
+
+ /* A long time ago UIDs where 16bit, hence explicitly avoid the 16bit -1 too */
+ if (uid == (uid_t) UINT32_C(0xFFFF))
+ return false;
+
+ return true;
+}
+
+int parse_uid(const char *s, uid_t *ret) {
+ uint32_t uid = 0;
+ int r;
+
+ assert(s);
+
+ assert_cc(sizeof(uid_t) == sizeof(uint32_t));
+ r = safe_atou32(s, &uid);
+ if (r < 0)
+ return r;
+
+ if (!uid_is_valid(uid))
+ return -ENXIO; /* we return ENXIO instead of EINVAL
+ * here, to make it easy to distuingish
+ * invalid numeric uids from invalid
+ * strings. */
+
+ if (ret)
+ *ret = uid;
+
+ return 0;
+}
+
+char* getlogname_malloc(void) {
+ uid_t uid;
+ struct stat st;
+
+ if (isatty(STDIN_FILENO) && fstat(STDIN_FILENO, &st) >= 0)
+ uid = st.st_uid;
+ else
+ uid = getuid();
+
+ return uid_to_name(uid);
+}
+
+char *getusername_malloc(void) {
+ const char *e;
+
+ e = getenv("USER");
+ if (e)
+ return strdup(e);
+
+ return uid_to_name(getuid());
+}
+
+int get_user_creds(
+ const char **username,
+ uid_t *uid, gid_t *gid,
+ const char **home,
+ const char **shell) {
+
+ struct passwd *p;
+ uid_t u;
+
+ assert(username);
+ assert(*username);
+
+ /* We enforce some special rules for uid=0: in order to avoid
+ * NSS lookups for root we hardcode its data. */
+
+ if (streq(*username, "root") || streq(*username, "0")) {
+ *username = "root";
+
+ if (uid)
+ *uid = 0;
+
+ if (gid)
+ *gid = 0;
+
+ if (home)
+ *home = "/root";
+
+ if (shell)
+ *shell = "/bin/sh";
+
+ return 0;
+ }
+
+ if (parse_uid(*username, &u) >= 0) {
+ errno = 0;
+ p = getpwuid(u);
+
+ /* If there are multiple users with the same id, make
+ * sure to leave $USER to the configured value instead
+ * of the first occurrence in the database. However if
+ * the uid was configured by a numeric uid, then let's
+ * pick the real username from /etc/passwd. */
+ if (p)
+ *username = p->pw_name;
+ } else {
+ errno = 0;
+ p = getpwnam(*username);
+ }
+
+ if (!p)
+ return errno > 0 ? -errno : -ESRCH;
+
+ if (uid) {
+ if (!uid_is_valid(p->pw_uid))
+ return -EBADMSG;
+
+ *uid = p->pw_uid;
+ }
+
+ if (gid) {
+ if (!gid_is_valid(p->pw_gid))
+ return -EBADMSG;
+
+ *gid = p->pw_gid;
+ }
+
+ if (home)
+ *home = p->pw_dir;
+
+ if (shell)
+ *shell = p->pw_shell;
+
+ return 0;
+}
+
+int get_group_creds(const char **groupname, gid_t *gid) {
+ struct group *g;
+ gid_t id;
+
+ assert(groupname);
+
+ /* We enforce some special rules for gid=0: in order to avoid
+ * NSS lookups for root we hardcode its data. */
+
+ if (streq(*groupname, "root") || streq(*groupname, "0")) {
+ *groupname = "root";
+
+ if (gid)
+ *gid = 0;
+
+ return 0;
+ }
+
+ if (parse_gid(*groupname, &id) >= 0) {
+ errno = 0;
+ g = getgrgid(id);
+
+ if (g)
+ *groupname = g->gr_name;
+ } else {
+ errno = 0;
+ g = getgrnam(*groupname);
+ }
+
+ if (!g)
+ return errno > 0 ? -errno : -ESRCH;
+
+ if (gid) {
+ if (!gid_is_valid(g->gr_gid))
+ return -EBADMSG;
+
+ *gid = g->gr_gid;
+ }
+
+ return 0;
+}
+
+char* uid_to_name(uid_t uid) {
+ char *ret;
+ int r;
+
+ /* Shortcut things to avoid NSS lookups */
+ if (uid == 0)
+ return strdup("root");
+
+ if (uid_is_valid(uid)) {
+ long bufsize;
+
+ bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
+ if (bufsize <= 0)
+ bufsize = 4096;
+
+ for (;;) {
+ struct passwd pwbuf, *pw = NULL;
+ _cleanup_free_ char *buf = NULL;
+
+ buf = malloc(bufsize);
+ if (!buf)
+ return NULL;
+
+ r = getpwuid_r(uid, &pwbuf, buf, (size_t) bufsize, &pw);
+ if (r == 0 && pw)
+ return strdup(pw->pw_name);
+ if (r != ERANGE)
+ break;
+
+ bufsize *= 2;
+ }
+ }
+
+ if (asprintf(&ret, UID_FMT, uid) < 0)
+ return NULL;
+
+ return ret;
+}
+
+char* gid_to_name(gid_t gid) {
+ char *ret;
+ int r;
+
+ if (gid == 0)
+ return strdup("root");
+
+ if (gid_is_valid(gid)) {
+ long bufsize;
+
+ bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
+ if (bufsize <= 0)
+ bufsize = 4096;
+
+ for (;;) {
+ struct group grbuf, *gr = NULL;
+ _cleanup_free_ char *buf = NULL;
+
+ buf = malloc(bufsize);
+ if (!buf)
+ return NULL;
+
+ r = getgrgid_r(gid, &grbuf, buf, (size_t) bufsize, &gr);
+ if (r == 0 && gr)
+ return strdup(gr->gr_name);
+ if (r != ERANGE)
+ break;
+
+ bufsize *= 2;
+ }
+ }
+
+ if (asprintf(&ret, GID_FMT, gid) < 0)
+ return NULL;
+
+ return ret;
+}
+
+int in_gid(gid_t gid) {
+ gid_t *gids;
+ int ngroups_max, r, i;
+
+ if (getgid() == gid)
+ return 1;
+
+ if (getegid() == gid)
+ return 1;
+
+ if (!gid_is_valid(gid))
+ return -EINVAL;
+
+ ngroups_max = sysconf(_SC_NGROUPS_MAX);
+ assert(ngroups_max > 0);
+
+ gids = alloca(sizeof(gid_t) * ngroups_max);
+
+ r = getgroups(ngroups_max, gids);
+ if (r < 0)
+ return -errno;
+
+ for (i = 0; i < r; i++)
+ if (gids[i] == gid)
+ return 1;
+
+ return 0;
+}
+
+int in_group(const char *name) {
+ int r;
+ gid_t gid;
+
+ r = get_group_creds(&name, &gid);
+ if (r < 0)
+ return r;
+
+ return in_gid(gid);
+}
+
+int get_home_dir(char **_h) {
+ struct passwd *p;
+ const char *e;
+ char *h;
+ uid_t u;
+
+ assert(_h);
+
+ /* Take the user specified one */
+ e = secure_getenv("HOME");
+ if (e && path_is_absolute(e)) {
+ h = strdup(e);
+ if (!h)
+ return -ENOMEM;
+
+ *_h = h;
+ return 0;
+ }
+
+ /* Hardcode home directory for root to avoid NSS */
+ u = getuid();
+ if (u == 0) {
+ h = strdup("/root");
+ if (!h)
+ return -ENOMEM;
+
+ *_h = h;
+ return 0;
+ }
+
+ /* Check the database... */
+ errno = 0;
+ p = getpwuid(u);
+ if (!p)
+ return errno > 0 ? -errno : -ESRCH;
+
+ if (!path_is_absolute(p->pw_dir))
+ return -EINVAL;
+
+ h = strdup(p->pw_dir);
+ if (!h)
+ return -ENOMEM;
+
+ *_h = h;
+ return 0;
+}
+
+int get_shell(char **_s) {
+ struct passwd *p;
+ const char *e;
+ char *s;
+ uid_t u;
+
+ assert(_s);
+
+ /* Take the user specified one */
+ e = getenv("SHELL");
+ if (e) {
+ s = strdup(e);
+ if (!s)
+ return -ENOMEM;
+
+ *_s = s;
+ return 0;
+ }
+
+ /* Hardcode home directory for root to avoid NSS */
+ u = getuid();
+ if (u == 0) {
+ s = strdup("/bin/sh");
+ if (!s)
+ return -ENOMEM;
+
+ *_s = s;
+ return 0;
+ }
+
+ /* Check the database... */
+ errno = 0;
+ p = getpwuid(u);
+ if (!p)
+ return errno > 0 ? -errno : -ESRCH;
+
+ if (!path_is_absolute(p->pw_shell))
+ return -EINVAL;
+
+ s = strdup(p->pw_shell);
+ if (!s)
+ return -ENOMEM;
+
+ *_s = s;
+ 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;
+}
+
+int take_etc_passwd_lock(const char *root) {
+
+ struct flock flock = {
+ .l_type = F_WRLCK,
+ .l_whence = SEEK_SET,
+ .l_start = 0,
+ .l_len = 0,
+ };
+
+ const char *path;
+ int fd, r;
+
+ /* This is roughly the same as lckpwdf(), but not as awful. We
+ * don't want to use alarm() and signals, hence we implement
+ * our own trivial version of this.
+ *
+ * Note that shadow-utils also takes per-database locks in
+ * addition to lckpwdf(). However, we don't given that they
+ * are redundant as they they invoke lckpwdf() first and keep
+ * it during everything they do. The per-database locks are
+ * awfully racy, and thus we just won't do them. */
+
+ if (root)
+ path = prefix_roota(root, "/etc/.pwd.lock");
+ else
+ path = "/etc/.pwd.lock";
+
+ fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600);
+ if (fd < 0)
+ return -errno;
+
+ r = fcntl(fd, F_SETLKW, &flock);
+ if (r < 0) {
+ safe_close(fd);
+ return -errno;
+ }
+
+ return fd;
+}
diff --git a/src/basic/user-util.h b/src/basic/user-util.h
new file mode 100644
index 0000000000..6106e138be
--- /dev/null
+++ b/src/basic/user-util.h
@@ -0,0 +1,67 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+#include <sys/types.h>
+
+bool uid_is_valid(uid_t uid);
+
+static inline bool gid_is_valid(gid_t gid) {
+ return uid_is_valid((uid_t) gid);
+}
+
+int parse_uid(const char *s, uid_t* ret_uid);
+
+static inline int parse_gid(const char *s, gid_t *ret_gid) {
+ return parse_uid(s, (uid_t*) ret_gid);
+}
+
+char* getlogname_malloc(void);
+char* getusername_malloc(void);
+
+int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell);
+int get_group_creds(const char **groupname, gid_t *gid);
+
+char* uid_to_name(uid_t uid);
+char* gid_to_name(gid_t gid);
+
+int in_gid(gid_t gid);
+int in_group(const char *name);
+
+int get_home_dir(char **ret);
+int get_shell(char **_ret);
+
+int reset_uid_gid(void);
+
+int take_etc_passwd_lock(const char *root);
+
+#define UID_INVALID ((uid_t) -1)
+#define GID_INVALID ((gid_t) -1)
+
+/* The following macros add 1 when converting things, since UID 0 is a
+ * valid UID, while the pointer NULL is special */
+#define PTR_TO_UID(p) ((uid_t) (((uintptr_t) (p))-1))
+#define UID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1))
+
+#define PTR_TO_GID(p) ((gid_t) (((uintptr_t) (p))-1))
+#define GID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1))
diff --git a/src/basic/utf8.c b/src/basic/utf8.c
index 800884ffee..124effd6df 100644
--- a/src/basic/utf8.c
+++ b/src/basic/utf8.c
@@ -44,13 +44,14 @@
*/
#include <errno.h>
+#include <stdbool.h>
#include <stdlib.h>
-#include <inttypes.h>
#include <string.h>
-#include <stdbool.h>
+#include "alloc-util.h"
+#include "hexdecoct.h"
+#include "macro.h"
#include "utf8.h"
-#include "util.h"
bool unichar_is_valid(uint32_t ch) {
diff --git a/src/basic/utf8.h b/src/basic/utf8.h
index e745649f06..16c4b5b55d 100644
--- a/src/basic/utf8.h
+++ b/src/basic/utf8.h
@@ -22,6 +22,8 @@
***/
#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
#include "macro.h"
diff --git a/src/basic/util.c b/src/basic/util.c
index 18be0bfd5a..4434ecfdf6 100644
--- a/src/basic/util.c
+++ b/src/basic/util.c
@@ -19,83 +19,46 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <ctype.h>
+#include <alloca.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
-#include <glob.h>
-#include <grp.h>
-#include <langinfo.h>
-#include <libintl.h>
-#include <limits.h>
-#include <linux/magic.h>
-#include <linux/sched.h>
-#include <locale.h>
-#include <netinet/ip.h>
-#include <poll.h>
-#include <pwd.h>
#include <sched.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/file.h>
-#include <sys/ioctl.h>
#include <sys/mman.h>
-#include <sys/mount.h>
-#include <sys/personality.h>
#include <sys/prctl.h>
-#include <sys/resource.h>
-#include <sys/stat.h>
-#include <sys/statvfs.h>
-#include <sys/time.h>
+#include <sys/statfs.h>
+#include <sys/sysmacros.h>
#include <sys/types.h>
-#include <sys/utsname.h>
-#include <sys/vfs.h>
-#include <sys/wait.h>
-#include <sys/xattr.h>
-#include <syslog.h>
#include <unistd.h>
-/* When we include libgen.h because we need dirname() we immediately
- * undefine basename() since libgen.h defines it as a macro to the
- * POSIX version which is really broken. We prefer GNU basename(). */
-#include <libgen.h>
-#undef basename
-
-#ifdef HAVE_SYS_AUXV_H
-#include <sys/auxv.h>
-#endif
-
-/* We include linux/fs.h as last of the system headers, as it
- * otherwise conflicts with sys/mount.h. Yay, Linux is great! */
-#include <linux/fs.h>
-
+#include "alloc-util.h"
+#include "build.h"
#include "def.h"
-#include "device-nodes.h"
-#include "env-util.h"
-#include "exit-status.h"
+#include "dirent-util.h"
+#include "fd-util.h"
#include "fileio.h"
#include "formats-util.h"
-#include "gunicode.h"
#include "hashmap.h"
#include "hostname-util.h"
-#include "ioprio.h"
#include "log.h"
#include "macro.h"
#include "missing.h"
-#include "mkdir.h"
+#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
-#include "random-util.h"
+#include "set.h"
#include "signal-util.h"
-#include "sparse-endian.h"
+#include "stat-util.h"
+#include "string-util.h"
#include "strv.h"
-#include "terminal-util.h"
-#include "utf8.h"
+#include "time-util.h"
+#include "user-util.h"
#include "util.h"
-#include "virt.h"
/* Put this test here for a lack of better place */
assert_cc(EAGAIN == EWOULDBLOCK);
@@ -117,2791 +80,6 @@ size_t page_size(void) {
return pgsz;
}
-int strcmp_ptr(const char *a, const char *b) {
-
- /* Like strcmp(), but tries to make sense of NULL pointers */
- if (a && b)
- return strcmp(a, b);
-
- if (!a && b)
- return -1;
-
- if (a && !b)
- return 1;
-
- return 0;
-}
-
-bool streq_ptr(const char *a, const char *b) {
- return strcmp_ptr(a, b) == 0;
-}
-
-char* endswith(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 (memcmp(s + sl - pl, postfix, pl) != 0)
- return NULL;
-
- 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;
-
- assert(s);
- assert(word);
-
- /* Checks if the string starts with the specified word, either
- * followed by NUL or by whitespace. Returns a pointer to the
- * NUL or the first character after the whitespace. */
-
- sl = strlen(s);
- wl = strlen(word);
-
- if (sl < wl)
- return NULL;
-
- if (wl == 0)
- return (char*) s;
-
- if (memcmp(s, word, wl) != 0)
- return NULL;
-
- p = s + wl;
- if (*p == 0)
- return (char*) p;
-
- if (!strchr(WHITESPACE, *p))
- return NULL;
-
- p += strspn(p, WHITESPACE);
- return (char*) p;
-}
-
-size_t cescape_char(char c, char *buf) {
- char * buf_old = buf;
-
- switch (c) {
-
- case '\a':
- *(buf++) = '\\';
- *(buf++) = 'a';
- break;
- case '\b':
- *(buf++) = '\\';
- *(buf++) = 'b';
- break;
- case '\f':
- *(buf++) = '\\';
- *(buf++) = 'f';
- break;
- case '\n':
- *(buf++) = '\\';
- *(buf++) = 'n';
- break;
- case '\r':
- *(buf++) = '\\';
- *(buf++) = 'r';
- break;
- case '\t':
- *(buf++) = '\\';
- *(buf++) = 't';
- break;
- case '\v':
- *(buf++) = '\\';
- *(buf++) = 'v';
- break;
- case '\\':
- *(buf++) = '\\';
- *(buf++) = '\\';
- break;
- case '"':
- *(buf++) = '\\';
- *(buf++) = '"';
- break;
- case '\'':
- *(buf++) = '\\';
- *(buf++) = '\'';
- break;
-
- default:
- /* For special chars we prefer octal over
- * hexadecimal encoding, simply because glib's
- * g_strescape() does the same */
- if ((c < ' ') || (c >= 127)) {
- *(buf++) = '\\';
- *(buf++) = octchar((unsigned char) c >> 6);
- *(buf++) = octchar((unsigned char) c >> 3);
- *(buf++) = octchar((unsigned char) c);
- } else
- *(buf++) = c;
- break;
- }
-
- return buf - buf_old;
-}
-
-int close_nointr(int fd) {
- assert(fd >= 0);
-
- if (close(fd) >= 0)
- return 0;
-
- /*
- * Just ignore EINTR; a retry loop is the wrong thing to do on
- * Linux.
- *
- * http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html
- * https://bugzilla.gnome.org/show_bug.cgi?id=682819
- * http://utcc.utoronto.ca/~cks/space/blog/unix/CloseEINTR
- * https://sites.google.com/site/michaelsafyan/software-engineering/checkforeintrwheninvokingclosethinkagain
- */
- if (errno == EINTR)
- return 0;
-
- return -errno;
-}
-
-int safe_close(int fd) {
-
- /*
- * Like close_nointr() but cannot fail. Guarantees errno is
- * unchanged. Is a NOP with negative fds passed, and returns
- * -1, so that it can be used in this syntax:
- *
- * fd = safe_close(fd);
- */
-
- if (fd >= 0) {
- PROTECT_ERRNO;
-
- /* The kernel might return pretty much any error code
- * via close(), but the fd will be closed anyway. The
- * only condition we want to check for here is whether
- * the fd was invalid at all... */
-
- assert_se(close_nointr(fd) != -EBADF);
- }
-
- return -1;
-}
-
-void close_many(const int fds[], unsigned n_fd) {
- unsigned i;
-
- assert(fds || n_fd <= 0);
-
- for (i = 0; i < n_fd; i++)
- safe_close(fds[i]);
-}
-
-int fclose_nointr(FILE *f) {
- assert(f);
-
- /* Same as close_nointr(), but for fclose() */
-
- if (fclose(f) == 0)
- return 0;
-
- if (errno == EINTR)
- return 0;
-
- return -errno;
-}
-
-FILE* safe_fclose(FILE *f) {
-
- /* Same as safe_close(), but for fclose() */
-
- if (f) {
- PROTECT_ERRNO;
-
- assert_se(fclose_nointr(f) != EBADF);
- }
-
- return NULL;
-}
-
-DIR* safe_closedir(DIR *d) {
-
- if (d) {
- PROTECT_ERRNO;
-
- assert_se(closedir(d) >= 0 || errno != EBADF);
- }
-
- return NULL;
-}
-
-int unlink_noerrno(const char *path) {
- PROTECT_ERRNO;
- int r;
-
- r = unlink(path);
- if (r < 0)
- return -errno;
-
- return 0;
-}
-
-int parse_boolean(const char *v) {
- assert(v);
-
- if (streq(v, "1") || strcaseeq(v, "yes") || strcaseeq(v, "y") || strcaseeq(v, "true") || strcaseeq(v, "t") || strcaseeq(v, "on"))
- return 1;
- else if (streq(v, "0") || strcaseeq(v, "no") || strcaseeq(v, "n") || strcaseeq(v, "false") || strcaseeq(v, "f") || strcaseeq(v, "off"))
- return 0;
-
- return -EINVAL;
-}
-
-int parse_pid(const char *s, pid_t* ret_pid) {
- unsigned long ul = 0;
- pid_t pid;
- int r;
-
- assert(s);
- assert(ret_pid);
-
- r = safe_atolu(s, &ul);
- if (r < 0)
- return r;
-
- pid = (pid_t) ul;
-
- if ((unsigned long) pid != ul)
- return -ERANGE;
-
- if (pid <= 0)
- return -ERANGE;
-
- *ret_pid = pid;
- return 0;
-}
-
-bool uid_is_valid(uid_t uid) {
-
- /* Some libc APIs use UID_INVALID as special placeholder */
- if (uid == (uid_t) 0xFFFFFFFF)
- return false;
-
- /* A long time ago UIDs where 16bit, hence explicitly avoid the 16bit -1 too */
- if (uid == (uid_t) 0xFFFF)
- return false;
-
- return true;
-}
-
-int parse_uid(const char *s, uid_t* ret_uid) {
- unsigned long ul = 0;
- uid_t uid;
- int r;
-
- assert(s);
-
- r = safe_atolu(s, &ul);
- if (r < 0)
- return r;
-
- uid = (uid_t) ul;
-
- if ((unsigned long) uid != ul)
- return -ERANGE;
-
- if (!uid_is_valid(uid))
- return -ENXIO; /* we return ENXIO instead of EINVAL
- * here, to make it easy to distuingish
- * invalid numeric uids invalid
- * strings. */
-
- if (ret_uid)
- *ret_uid = uid;
-
- return 0;
-}
-
-int safe_atou(const char *s, unsigned *ret_u) {
- char *x = NULL;
- unsigned long l;
-
- assert(s);
- assert(ret_u);
-
- errno = 0;
- l = strtoul(s, &x, 0);
-
- if (!x || x == s || *x || errno)
- return errno > 0 ? -errno : -EINVAL;
-
- if ((unsigned long) (unsigned) l != l)
- return -ERANGE;
-
- *ret_u = (unsigned) l;
- return 0;
-}
-
-int safe_atoi(const char *s, int *ret_i) {
- char *x = NULL;
- long l;
-
- assert(s);
- assert(ret_i);
-
- errno = 0;
- l = strtol(s, &x, 0);
-
- if (!x || x == s || *x || errno)
- return errno > 0 ? -errno : -EINVAL;
-
- if ((long) (int) l != l)
- return -ERANGE;
-
- *ret_i = (int) l;
- return 0;
-}
-
-int safe_atou8(const char *s, uint8_t *ret) {
- char *x = NULL;
- unsigned long l;
-
- assert(s);
- assert(ret);
-
- errno = 0;
- l = strtoul(s, &x, 0);
-
- if (!x || x == s || *x || errno)
- return errno > 0 ? -errno : -EINVAL;
-
- if ((unsigned long) (uint8_t) l != l)
- return -ERANGE;
-
- *ret = (uint8_t) l;
- return 0;
-}
-
-int safe_atou16(const char *s, uint16_t *ret) {
- char *x = NULL;
- unsigned long l;
-
- assert(s);
- assert(ret);
-
- errno = 0;
- l = strtoul(s, &x, 0);
-
- if (!x || x == s || *x || errno)
- return errno > 0 ? -errno : -EINVAL;
-
- if ((unsigned long) (uint16_t) l != l)
- return -ERANGE;
-
- *ret = (uint16_t) l;
- return 0;
-}
-
-int safe_atoi16(const char *s, int16_t *ret) {
- char *x = NULL;
- long l;
-
- assert(s);
- assert(ret);
-
- errno = 0;
- l = strtol(s, &x, 0);
-
- if (!x || x == s || *x || errno)
- return errno > 0 ? -errno : -EINVAL;
-
- if ((long) (int16_t) l != l)
- return -ERANGE;
-
- *ret = (int16_t) l;
- return 0;
-}
-
-int safe_atollu(const char *s, long long unsigned *ret_llu) {
- char *x = NULL;
- unsigned long long l;
-
- assert(s);
- assert(ret_llu);
-
- errno = 0;
- l = strtoull(s, &x, 0);
-
- if (!x || x == s || *x || errno)
- return errno ? -errno : -EINVAL;
-
- *ret_llu = l;
- return 0;
-}
-
-int safe_atolli(const char *s, long long int *ret_lli) {
- char *x = NULL;
- long long l;
-
- assert(s);
- assert(ret_lli);
-
- errno = 0;
- l = strtoll(s, &x, 0);
-
- if (!x || x == s || *x || errno)
- return errno ? -errno : -EINVAL;
-
- *ret_lli = l;
- return 0;
-}
-
-int safe_atod(const char *s, double *ret_d) {
- char *x = NULL;
- double d = 0;
- locale_t loc;
-
- assert(s);
- assert(ret_d);
-
- loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
- if (loc == (locale_t) 0)
- return -errno;
-
- errno = 0;
- d = strtod_l(s, &x, loc);
-
- if (!x || x == s || *x || errno) {
- freelocale(loc);
- return errno ? -errno : -EINVAL;
- }
-
- freelocale(loc);
- *ret_d = (double) d;
- return 0;
-}
-
-static size_t strcspn_escaped(const char *s, const char *reject) {
- bool escaped = false;
- int n;
-
- for (n=0; s[n]; n++) {
- if (escaped)
- escaped = false;
- else if (s[n] == '\\')
- escaped = true;
- else if (strchr(reject, s[n]))
- break;
- }
-
- /* if s ends in \, return index of previous char */
- return n - escaped;
-}
-
-/* Split a string into words. */
-const char* split(const char **state, size_t *l, const char *separator, bool quoted) {
- const char *current;
-
- current = *state;
-
- if (!*current) {
- assert(**state == '\0');
- return NULL;
- }
-
- current += strspn(current, separator);
- if (!*current) {
- *state = current;
- return NULL;
- }
-
- if (quoted && strchr("\'\"", *current)) {
- char quotechars[2] = {*current, '\0'};
-
- *l = strcspn_escaped(current + 1, quotechars);
- 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;
- }
- *state = current++ + *l + 2;
- } else if (quoted) {
- *l = strcspn_escaped(current, separator);
- if (current[*l] && !strchr(separator, current[*l])) {
- /* unfinished escape */
- *state = current;
- return NULL;
- }
- *state = current + *l;
- } else {
- *l = strcspn(current, separator);
- *state = current + *l;
- }
-
- return current;
-}
-
-int fchmod_umask(int fd, mode_t m) {
- mode_t u;
- int r;
-
- u = umask(0777);
- r = fchmod(fd, m & (~u)) < 0 ? -errno : 0;
- umask(u);
-
- return r;
-}
-
-char *truncate_nl(char *s) {
- assert(s);
-
- s[strcspn(s, NEWLINE)] = 0;
- return s;
-}
-
-char *strnappend(const char *s, const char *suffix, size_t b) {
- size_t a;
- char *r;
-
- if (!s && !suffix)
- return strdup("");
-
- if (!s)
- return strndup(suffix, b);
-
- if (!suffix)
- return strdup(s);
-
- assert(s);
- assert(suffix);
-
- a = strlen(s);
- if (b > ((size_t) -1) - a)
- return NULL;
-
- r = new(char, a+b+1);
- if (!r)
- return NULL;
-
- memcpy(r, s, a);
- memcpy(r+a, suffix, b);
- r[a+b] = 0;
-
- return r;
-}
-
-char *strappend(const char *s, const char *suffix) {
- return strnappend(s, suffix, suffix ? strlen(suffix) : 0);
-}
-
-int readlinkat_malloc(int fd, const char *p, char **ret) {
- size_t l = 100;
- int r;
-
- assert(p);
- assert(ret);
-
- for (;;) {
- char *c;
- ssize_t n;
-
- c = new(char, l);
- if (!c)
- return -ENOMEM;
-
- n = readlinkat(fd, p, c, l-1);
- if (n < 0) {
- r = -errno;
- free(c);
- return r;
- }
-
- if ((size_t) n < l-1) {
- c[n] = 0;
- *ret = c;
- return 0;
- }
-
- free(c);
- l *= 2;
- }
-}
-
-int readlink_malloc(const char *p, char **ret) {
- return readlinkat_malloc(AT_FDCWD, p, ret);
-}
-
-int readlink_value(const char *p, char **ret) {
- _cleanup_free_ char *link = NULL;
- char *value;
- int r;
-
- r = readlink_malloc(p, &link);
- if (r < 0)
- return r;
-
- value = basename(link);
- if (!value)
- return -ENOENT;
-
- value = strdup(value);
- if (!value)
- return -ENOMEM;
-
- *ret = value;
-
- return 0;
-}
-
-int readlink_and_make_absolute(const char *p, char **r) {
- _cleanup_free_ char *target = NULL;
- char *k;
- int j;
-
- assert(p);
- assert(r);
-
- j = readlink_malloc(p, &target);
- if (j < 0)
- return j;
-
- k = file_in_same_dir(p, target);
- if (!k)
- return -ENOMEM;
-
- *r = k;
- return 0;
-}
-
-int readlink_and_canonicalize(const char *p, char **r) {
- char *t, *s;
- int j;
-
- assert(p);
- assert(r);
-
- j = readlink_and_make_absolute(p, &t);
- if (j < 0)
- return j;
-
- s = canonicalize_file_name(t);
- if (s) {
- free(t);
- *r = s;
- } else
- *r = t;
-
- path_kill_slashes(*r);
-
- return 0;
-}
-
-char *strstrip(char *s) {
- char *e;
-
- /* Drops trailing whitespace. Modifies the string in
- * place. Returns pointer to first non-space character */
-
- s += strspn(s, WHITESPACE);
-
- for (e = strchr(s, 0); e > s; e --)
- if (!strchr(WHITESPACE, e[-1]))
- break;
-
- *e = 0;
-
- return s;
-}
-
-char *delete_chars(char *s, const char *bad) {
- char *f, *t;
-
- /* Drops all whitespace, regardless where in the string */
-
- for (f = s, t = s; *f; f++) {
- if (strchr(bad, *f))
- continue;
-
- *(t++) = *f;
- }
-
- *t = 0;
-
- return s;
-}
-
-char *file_in_same_dir(const char *path, const char *filename) {
- char *e, *ret;
- size_t k;
-
- assert(path);
- assert(filename);
-
- /* This removes the last component of path and appends
- * filename, unless the latter is absolute anyway or the
- * former isn't */
-
- if (path_is_absolute(filename))
- return strdup(filename);
-
- e = strrchr(path, '/');
- if (!e)
- return strdup(filename);
-
- k = strlen(filename);
- ret = new(char, (e + 1 - path) + k + 1);
- if (!ret)
- return NULL;
-
- memcpy(mempcpy(ret, path, e + 1 - path), filename, k + 1);
- return ret;
-}
-
-int rmdir_parents(const char *path, const char *stop) {
- size_t l;
- int r = 0;
-
- assert(path);
- assert(stop);
-
- l = strlen(path);
-
- /* Skip trailing slashes */
- while (l > 0 && path[l-1] == '/')
- l--;
-
- while (l > 0) {
- char *t;
-
- /* Skip last component */
- while (l > 0 && path[l-1] != '/')
- l--;
-
- /* Skip trailing slashes */
- while (l > 0 && path[l-1] == '/')
- l--;
-
- if (l <= 0)
- break;
-
- if (!(t = strndup(path, l)))
- return -ENOMEM;
-
- if (path_startswith(stop, t)) {
- free(t);
- return 0;
- }
-
- r = rmdir(t);
- free(t);
-
- if (r < 0)
- if (errno != ENOENT)
- return -errno;
- }
-
- return 0;
-}
-
-char hexchar(int x) {
- static const char table[16] = "0123456789abcdef";
-
- return table[x & 15];
-}
-
-int unhexchar(char c) {
-
- if (c >= '0' && c <= '9')
- return c - '0';
-
- if (c >= 'a' && c <= 'f')
- return c - 'a' + 10;
-
- if (c >= 'A' && c <= 'F')
- return c - 'A' + 10;
-
- return -EINVAL;
-}
-
-char *hexmem(const void *p, size_t l) {
- char *r, *z;
- const uint8_t *x;
-
- z = r = malloc(l * 2 + 1);
- if (!r)
- return NULL;
-
- for (x = p; x < (const uint8_t*) p + l; x++) {
- *(z++) = hexchar(*x >> 4);
- *(z++) = hexchar(*x & 15);
- }
-
- *z = 0;
- return r;
-}
-
-int unhexmem(const char *p, size_t l, void **mem, size_t *len) {
- _cleanup_free_ uint8_t *r = NULL;
- uint8_t *z;
- const char *x;
-
- assert(mem);
- assert(len);
- assert(p);
-
- z = r = malloc((l + 1) / 2 + 1);
- if (!r)
- return -ENOMEM;
-
- for (x = p; x < p + l; x += 2) {
- int a, b;
-
- a = unhexchar(x[0]);
- if (a < 0)
- return a;
- else if (x+1 < p + l) {
- b = unhexchar(x[1]);
- if (b < 0)
- return b;
- } else
- b = 0;
-
- *(z++) = (uint8_t) a << 4 | (uint8_t) b;
- }
-
- *z = 0;
-
- *mem = r;
- r = NULL;
- *len = (l + 1) / 2;
-
- return 0;
-}
-
-/* https://tools.ietf.org/html/rfc4648#section-6
- * Notice that base32hex differs from base32 in the alphabet it uses.
- * The distinction is that the base32hex representation preserves the
- * order of the underlying data when compared as bytestrings, this is
- * useful when representing NSEC3 hashes, as one can then verify the
- * order of hashes directly from their representation. */
-char base32hexchar(int x) {
- static const char table[32] = "0123456789"
- "ABCDEFGHIJKLMNOPQRSTUV";
-
- return table[x & 31];
-}
-
-int unbase32hexchar(char c) {
- unsigned offset;
-
- if (c >= '0' && c <= '9')
- return c - '0';
-
- offset = '9' - '0' + 1;
-
- if (c >= 'A' && c <= 'V')
- return c - 'A' + offset;
-
- return -EINVAL;
-}
-
-char *base32hexmem(const void *p, size_t l, bool padding) {
- char *r, *z;
- const uint8_t *x;
- size_t len;
-
- if (padding)
- /* five input bytes makes eight output bytes, padding is added so we must round up */
- len = 8 * (l + 4) / 5;
- else {
- /* same, but round down as there is no padding */
- len = 8 * l / 5;
-
- switch (l % 5) {
- case 4:
- len += 7;
- break;
- case 3:
- len += 5;
- break;
- case 2:
- len += 4;
- break;
- case 1:
- len += 2;
- break;
- }
- }
-
- z = r = malloc(len + 1);
- if (!r)
- return NULL;
-
- for (x = p; x < (const uint8_t*) p + (l / 5) * 5; x += 5) {
- /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ
- x[3] == QQQQQQQQ; x[4] == WWWWWWWW */
- *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */
- *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */
- *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */
- *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */
- *(z++) = base32hexchar((x[2] & 15) << 1 | x[3] >> 7); /* 000ZZZZQ */
- *(z++) = base32hexchar((x[3] & 127) >> 2); /* 000QQQQQ */
- *(z++) = base32hexchar((x[3] & 3) << 3 | x[4] >> 5); /* 000QQWWW */
- *(z++) = base32hexchar((x[4] & 31)); /* 000WWWWW */
- }
-
- switch (l % 5) {
- case 4:
- *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */
- *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */
- *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */
- *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */
- *(z++) = base32hexchar((x[2] & 15) << 1 | x[3] >> 7); /* 000ZZZZQ */
- *(z++) = base32hexchar((x[3] & 127) >> 2); /* 000QQQQQ */
- *(z++) = base32hexchar((x[3] & 3) << 3); /* 000QQ000 */
- if (padding)
- *(z++) = '=';
-
- break;
-
- case 3:
- *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */
- *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */
- *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */
- *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */
- *(z++) = base32hexchar((x[2] & 15) << 1); /* 000ZZZZ0 */
- if (padding) {
- *(z++) = '=';
- *(z++) = '=';
- *(z++) = '=';
- }
-
- break;
-
- case 2:
- *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */
- *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */
- *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */
- *(z++) = base32hexchar((x[1] & 1) << 4); /* 000Y0000 */
- if (padding) {
- *(z++) = '=';
- *(z++) = '=';
- *(z++) = '=';
- *(z++) = '=';
- }
-
- break;
-
- case 1:
- *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */
- *(z++) = base32hexchar((x[0] & 7) << 2); /* 000XXX00 */
- if (padding) {
- *(z++) = '=';
- *(z++) = '=';
- *(z++) = '=';
- *(z++) = '=';
- *(z++) = '=';
- *(z++) = '=';
- }
-
- break;
- }
-
- *z = 0;
- return r;
-}
-
-int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *_len) {
- _cleanup_free_ uint8_t *r = NULL;
- int a, b, c, d, e, f, g, h;
- uint8_t *z;
- const char *x;
- size_t len;
- unsigned pad = 0;
-
- assert(p);
-
- /* padding ensures any base32hex input has input divisible by 8 */
- if (padding && l % 8 != 0)
- return -EINVAL;
-
- if (padding) {
- /* strip the padding */
- while (l > 0 && p[l - 1] == '=' && pad < 7) {
- pad ++;
- l --;
- }
- }
-
- /* a group of eight input bytes needs five output bytes, in case of
- padding we need to add some extra bytes */
- len = (l / 8) * 5;
-
- switch (l % 8) {
- case 7:
- len += 4;
- break;
- case 5:
- len += 3;
- break;
- case 4:
- len += 2;
- break;
- case 2:
- len += 1;
- break;
- case 0:
- break;
- default:
- return -EINVAL;
- }
-
- z = r = malloc(len + 1);
- if (!r)
- return -ENOMEM;
-
- for (x = p; x < p + (l / 8) * 8; x += 8) {
- /* a == 000XXXXX; b == 000YYYYY; c == 000ZZZZZ; d == 000WWWWW
- e == 000SSSSS; f == 000QQQQQ; g == 000VVVVV; h == 000RRRRR */
- a = unbase32hexchar(x[0]);
- if (a < 0)
- return -EINVAL;
-
- b = unbase32hexchar(x[1]);
- if (b < 0)
- return -EINVAL;
-
- c = unbase32hexchar(x[2]);
- if (c < 0)
- return -EINVAL;
-
- d = unbase32hexchar(x[3]);
- if (d < 0)
- return -EINVAL;
-
- e = unbase32hexchar(x[4]);
- if (e < 0)
- return -EINVAL;
-
- f = unbase32hexchar(x[5]);
- if (f < 0)
- return -EINVAL;
-
- g = unbase32hexchar(x[6]);
- if (g < 0)
- return -EINVAL;
-
- h = unbase32hexchar(x[7]);
- if (h < 0)
- return -EINVAL;
-
- *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */
- *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
- *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1; /* WWWWSSSS */
- *(z++) = (uint8_t) e << 7 | (uint8_t) f << 2 | (uint8_t) g >> 3; /* SQQQQQVV */
- *(z++) = (uint8_t) g << 5 | (uint8_t) h; /* VVVRRRRR */
- }
-
- switch (l % 8) {
- case 7:
- a = unbase32hexchar(x[0]);
- if (a < 0)
- return -EINVAL;
-
- b = unbase32hexchar(x[1]);
- if (b < 0)
- return -EINVAL;
-
- c = unbase32hexchar(x[2]);
- if (c < 0)
- return -EINVAL;
-
- d = unbase32hexchar(x[3]);
- if (d < 0)
- return -EINVAL;
-
- e = unbase32hexchar(x[4]);
- if (e < 0)
- return -EINVAL;
-
- f = unbase32hexchar(x[5]);
- if (f < 0)
- return -EINVAL;
-
- g = unbase32hexchar(x[6]);
- if (g < 0)
- return -EINVAL;
-
- /* g == 000VV000 */
- if (g & 7)
- return -EINVAL;
-
- *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */
- *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
- *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1; /* WWWWSSSS */
- *(z++) = (uint8_t) e << 7 | (uint8_t) f << 2 | (uint8_t) g >> 3; /* SQQQQQVV */
-
- break;
- case 5:
- a = unbase32hexchar(x[0]);
- if (a < 0)
- return -EINVAL;
-
- b = unbase32hexchar(x[1]);
- if (b < 0)
- return -EINVAL;
-
- c = unbase32hexchar(x[2]);
- if (c < 0)
- return -EINVAL;
-
- d = unbase32hexchar(x[3]);
- if (d < 0)
- return -EINVAL;
-
- e = unbase32hexchar(x[4]);
- if (e < 0)
- return -EINVAL;
-
- /* e == 000SSSS0 */
- if (e & 1)
- return -EINVAL;
-
- *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */
- *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
- *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1; /* WWWWSSSS */
-
- break;
- case 4:
- a = unbase32hexchar(x[0]);
- if (a < 0)
- return -EINVAL;
-
- b = unbase32hexchar(x[1]);
- if (b < 0)
- return -EINVAL;
-
- c = unbase32hexchar(x[2]);
- if (c < 0)
- return -EINVAL;
-
- d = unbase32hexchar(x[3]);
- if (d < 0)
- return -EINVAL;
-
- /* d == 000W0000 */
- if (d & 15)
- return -EINVAL;
-
- *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */
- *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
-
- break;
- case 2:
- a = unbase32hexchar(x[0]);
- if (a < 0)
- return -EINVAL;
-
- b = unbase32hexchar(x[1]);
- if (b < 0)
- return -EINVAL;
-
- /* b == 000YYY00 */
- if (b & 3)
- return -EINVAL;
-
- *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */
-
- break;
- case 0:
- break;
- default:
- return -EINVAL;
- }
-
- *z = 0;
-
- *mem = r;
- r = NULL;
- *_len = len;
-
- return 0;
-}
-
-/* https://tools.ietf.org/html/rfc4648#section-4 */
-char base64char(int x) {
- static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "abcdefghijklmnopqrstuvwxyz"
- "0123456789+/";
- return table[x & 63];
-}
-
-int unbase64char(char c) {
- unsigned offset;
-
- if (c >= 'A' && c <= 'Z')
- return c - 'A';
-
- offset = 'Z' - 'A' + 1;
-
- if (c >= 'a' && c <= 'z')
- return c - 'a' + offset;
-
- offset += 'z' - 'a' + 1;
-
- if (c >= '0' && c <= '9')
- return c - '0' + offset;
-
- offset += '9' - '0' + 1;
-
- if (c == '+')
- return offset;
-
- offset ++;
-
- if (c == '/')
- return offset;
-
- return -EINVAL;
-}
-
-char *base64mem(const void *p, size_t l) {
- char *r, *z;
- const uint8_t *x;
-
- /* three input bytes makes four output bytes, padding is added so we must round up */
- z = r = malloc(4 * (l + 2) / 3 + 1);
- if (!r)
- return NULL;
-
- for (x = p; x < (const uint8_t*) p + (l / 3) * 3; x += 3) {
- /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ */
- *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */
- *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */
- *(z++) = base64char((x[1] & 15) << 2 | x[2] >> 6); /* 00YYYYZZ */
- *(z++) = base64char(x[2] & 63); /* 00ZZZZZZ */
- }
-
- switch (l % 3) {
- case 2:
- *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */
- *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */
- *(z++) = base64char((x[1] & 15) << 2); /* 00YYYY00 */
- *(z++) = '=';
-
- break;
- case 1:
- *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */
- *(z++) = base64char((x[0] & 3) << 4); /* 00XX0000 */
- *(z++) = '=';
- *(z++) = '=';
-
- break;
- }
-
- *z = 0;
- return r;
-}
-
-int unbase64mem(const char *p, size_t l, void **mem, size_t *_len) {
- _cleanup_free_ uint8_t *r = NULL;
- int a, b, c, d;
- uint8_t *z;
- const char *x;
- size_t len;
-
- assert(p);
-
- /* padding ensures any base63 input has input divisible by 4 */
- if (l % 4 != 0)
- return -EINVAL;
-
- /* strip the padding */
- if (l > 0 && p[l - 1] == '=')
- l --;
- if (l > 0 && p[l - 1] == '=')
- l --;
-
- /* a group of four input bytes needs three output bytes, in case of
- padding we need to add two or three extra bytes */
- len = (l / 4) * 3 + (l % 4 ? (l % 4) - 1 : 0);
-
- z = r = malloc(len + 1);
- if (!r)
- return -ENOMEM;
-
- for (x = p; x < p + (l / 4) * 4; x += 4) {
- /* a == 00XXXXXX; b == 00YYYYYY; c == 00ZZZZZZ; d == 00WWWWWW */
- a = unbase64char(x[0]);
- if (a < 0)
- return -EINVAL;
-
- b = unbase64char(x[1]);
- if (b < 0)
- return -EINVAL;
-
- c = unbase64char(x[2]);
- if (c < 0)
- return -EINVAL;
-
- d = unbase64char(x[3]);
- if (d < 0)
- return -EINVAL;
-
- *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
- *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
- *(z++) = (uint8_t) c << 6 | (uint8_t) d; /* ZZWWWWWW */
- }
-
- switch (l % 4) {
- case 3:
- a = unbase64char(x[0]);
- if (a < 0)
- return -EINVAL;
-
- b = unbase64char(x[1]);
- if (b < 0)
- return -EINVAL;
-
- c = unbase64char(x[2]);
- if (c < 0)
- return -EINVAL;
-
- /* c == 00ZZZZ00 */
- if (c & 3)
- return -EINVAL;
-
- *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
- *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
-
- break;
- case 2:
- a = unbase64char(x[0]);
- if (a < 0)
- return -EINVAL;
-
- b = unbase64char(x[1]);
- if (b < 0)
- return -EINVAL;
-
- /* b == 00YY0000 */
- if (b & 15)
- return -EINVAL;
-
- *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */
-
- break;
- case 0:
-
- break;
- default:
- return -EINVAL;
- }
-
- *z = 0;
-
- *mem = r;
- r = NULL;
- *_len = len;
-
- return 0;
-}
-
-char octchar(int x) {
- return '0' + (x & 7);
-}
-
-int unoctchar(char c) {
-
- if (c >= '0' && c <= '7')
- return c - '0';
-
- return -EINVAL;
-}
-
-char decchar(int x) {
- return '0' + (x % 10);
-}
-
-int undecchar(char c) {
-
- if (c >= '0' && c <= '9')
- return c - '0';
-
- return -EINVAL;
-}
-
-char *cescape(const char *s) {
- char *r, *t;
- const char *f;
-
- assert(s);
-
- /* Does C style string escaping. May be reversed with
- * cunescape(). */
-
- r = new(char, strlen(s)*4 + 1);
- if (!r)
- return NULL;
-
- for (f = s, t = r; *f; f++)
- t += cescape_char(*f, t);
-
- *t = 0;
-
- return r;
-}
-
-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. */
-
- pl = prefix ? strlen(prefix) : 0;
-
- r = new(char, pl+length+1);
- if (!r)
- return -ENOMEM;
-
- if (prefix)
- memcpy(r, prefix, pl);
-
- for (f = s, t = r + pl; f < 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, copy verbatim */
- *(t++) = *f;
- continue;
- }
-
- if (remaining == 1) {
- if (flags & UNESCAPE_RELAX) {
- /* A trailing backslash, copy verbatim */
- *(t++) = *f;
- continue;
- }
-
- free(r);
- return -EINVAL;
- }
-
- 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++) = '\\';
- continue;
- }
-
- free(r);
- return k;
- }
-
- 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;
-
- *ret = r;
- return t - r;
-}
-
-int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) {
- return cunescape_length_with_prefix(s, length, NULL, flags, ret);
-}
-
-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) {
- char *r, *t;
- const char *f;
-
- /* Escapes all chars in bad, in addition to \ and all special
- * chars, in \xFF style escaping. May be reversed with
- * cunescape(). */
-
- r = new(char, strlen(s) * 4 + 1);
- if (!r)
- return NULL;
-
- for (f = s, t = r; *f; f++) {
-
- if ((*f < ' ') || (*f >= 127) ||
- (*f == '\\') || strchr(bad, *f)) {
- *(t++) = '\\';
- *(t++) = 'x';
- *(t++) = hexchar(*f >> 4);
- *(t++) = hexchar(*f);
- } else
- *(t++) = *f;
- }
-
- *t = 0;
-
- return r;
-}
-
-char *ascii_strlower(char *t) {
- char *p;
-
- assert(t);
-
- for (p = t; *p; p++)
- if (*p >= 'A' && *p <= 'Z')
- *p = *p - 'A' + 'a';
-
- return t;
-}
-
-_pure_ static bool hidden_file_allow_backup(const char *filename) {
- assert(filename);
-
- return
- filename[0] == '.' ||
- streq(filename, "lost+found") ||
- streq(filename, "aquota.user") ||
- streq(filename, "aquota.group") ||
- endswith(filename, ".rpmnew") ||
- endswith(filename, ".rpmsave") ||
- endswith(filename, ".rpmorig") ||
- endswith(filename, ".dpkg-old") ||
- endswith(filename, ".dpkg-new") ||
- endswith(filename, ".dpkg-tmp") ||
- endswith(filename, ".dpkg-dist") ||
- endswith(filename, ".dpkg-bak") ||
- endswith(filename, ".dpkg-backup") ||
- endswith(filename, ".dpkg-remove") ||
- endswith(filename, ".swp");
-}
-
-bool hidden_file(const char *filename) {
- assert(filename);
-
- if (endswith(filename, "~"))
- return true;
-
- return hidden_file_allow_backup(filename);
-}
-
-int fd_nonblock(int fd, bool nonblock) {
- int flags, nflags;
-
- assert(fd >= 0);
-
- flags = fcntl(fd, F_GETFL, 0);
- if (flags < 0)
- return -errno;
-
- if (nonblock)
- nflags = flags | O_NONBLOCK;
- else
- nflags = flags & ~O_NONBLOCK;
-
- if (nflags == flags)
- return 0;
-
- if (fcntl(fd, F_SETFL, nflags) < 0)
- return -errno;
-
- return 0;
-}
-
-int fd_cloexec(int fd, bool cloexec) {
- int flags, nflags;
-
- assert(fd >= 0);
-
- flags = fcntl(fd, F_GETFD, 0);
- if (flags < 0)
- return -errno;
-
- if (cloexec)
- nflags = flags | FD_CLOEXEC;
- else
- nflags = flags & ~FD_CLOEXEC;
-
- if (nflags == flags)
- return 0;
-
- if (fcntl(fd, F_SETFD, nflags) < 0)
- return -errno;
-
- return 0;
-}
-
-_pure_ static bool fd_in_set(int fd, const int fdset[], unsigned n_fdset) {
- unsigned i;
-
- assert(n_fdset == 0 || fdset);
-
- for (i = 0; i < n_fdset; i++)
- if (fdset[i] == fd)
- return true;
-
- return false;
-}
-
-int close_all_fds(const int except[], unsigned n_except) {
- _cleanup_closedir_ DIR *d = NULL;
- struct dirent *de;
- int r = 0;
-
- assert(n_except == 0 || except);
-
- d = opendir("/proc/self/fd");
- if (!d) {
- int fd;
- struct rlimit rl;
-
- /* When /proc isn't available (for example in chroots)
- * the fallback is brute forcing through the fd
- * table */
-
- assert_se(getrlimit(RLIMIT_NOFILE, &rl) >= 0);
- for (fd = 3; fd < (int) rl.rlim_max; fd ++) {
-
- if (fd_in_set(fd, except, n_except))
- continue;
-
- if (close_nointr(fd) < 0)
- if (errno != EBADF && r == 0)
- r = -errno;
- }
-
- return r;
- }
-
- while ((de = readdir(d))) {
- int fd = -1;
-
- if (hidden_file(de->d_name))
- continue;
-
- if (safe_atoi(de->d_name, &fd) < 0)
- /* Let's better ignore this, just in case */
- continue;
-
- if (fd < 3)
- continue;
-
- if (fd == dirfd(d))
- continue;
-
- if (fd_in_set(fd, except, n_except))
- continue;
-
- if (close_nointr(fd) < 0) {
- /* Valgrind has its own FD and doesn't want to have it closed */
- if (errno != EBADF && r == 0)
- r = -errno;
- }
- }
-
- return r;
-}
-
-bool chars_intersect(const char *a, const char *b) {
- const char *p;
-
- /* Returns true if any of the chars in a are in b. */
- for (p = a; *p; p++)
- if (strchr(b, *p))
- return true;
-
- return false;
-}
-
-bool fstype_is_network(const char *fstype) {
- static const char table[] =
- "afs\0"
- "cifs\0"
- "smbfs\0"
- "sshfs\0"
- "ncpfs\0"
- "ncp\0"
- "nfs\0"
- "nfs4\0"
- "gfs\0"
- "gfs2\0"
- "glusterfs\0";
-
- const char *x;
-
- x = startswith(fstype, "fuse.");
- if (x)
- fstype = x;
-
- return nulstr_contains(table, fstype);
-}
-
-int flush_fd(int fd) {
- struct pollfd pollfd = {
- .fd = fd,
- .events = POLLIN,
- };
-
- for (;;) {
- char buf[LINE_MAX];
- ssize_t l;
- int r;
-
- r = poll(&pollfd, 1, 0);
- if (r < 0) {
- if (errno == EINTR)
- continue;
-
- return -errno;
-
- } else if (r == 0)
- return 0;
-
- l = read(fd, buf, sizeof(buf));
- if (l < 0) {
-
- if (errno == EINTR)
- continue;
-
- if (errno == EAGAIN)
- return 0;
-
- return -errno;
- } else if (l == 0)
- return 0;
- }
-}
-
-void safe_close_pair(int p[]) {
- assert(p);
-
- if (p[0] == p[1]) {
- /* Special case pairs which use the same fd in both
- * directions... */
- p[0] = p[1] = safe_close(p[0]);
- return;
- }
-
- p[0] = safe_close(p[0]);
- p[1] = safe_close(p[1]);
-}
-
-ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
- uint8_t *p = buf;
- ssize_t n = 0;
-
- assert(fd >= 0);
- assert(buf);
-
- /* If called with nbytes == 0, let's call read() at least
- * once, to validate the operation */
-
- if (nbytes > (size_t) SSIZE_MAX)
- return -EINVAL;
-
- do {
- ssize_t k;
-
- k = read(fd, p, nbytes);
- if (k < 0) {
- if (errno == EINTR)
- continue;
-
- if (errno == EAGAIN && do_poll) {
-
- /* We knowingly ignore any return value here,
- * and expect that any error/EOF is reported
- * via read() */
-
- (void) fd_wait_for_event(fd, POLLIN, USEC_INFINITY);
- continue;
- }
-
- return n > 0 ? n : -errno;
- }
-
- if (k == 0)
- return n;
-
- assert((size_t) k <= nbytes);
-
- p += k;
- nbytes -= k;
- n += k;
- } while (nbytes > 0);
-
- 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 (int) 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;
-
- assert(fd >= 0);
- assert(buf);
-
- if (nbytes > (size_t) SSIZE_MAX)
- return -EINVAL;
-
- do {
- ssize_t k;
-
- k = write(fd, p, nbytes);
- if (k < 0) {
- if (errno == EINTR)
- continue;
-
- if (errno == EAGAIN && do_poll) {
- /* We knowingly ignore any return value here,
- * and expect that any error/EOF is reported
- * via write() */
-
- (void) fd_wait_for_event(fd, POLLOUT, USEC_INFINITY);
- continue;
- }
-
- return -errno;
- }
-
- if (_unlikely_(nbytes > 0 && k == 0)) /* Can't really happen */
- return -EIO;
-
- assert((size_t) k <= nbytes);
-
- p += k;
- nbytes -= k;
- } while (nbytes > 0);
-
- return 0;
-}
-
-int parse_size(const char *t, uint64_t base, uint64_t *size) {
-
- /* 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
- * 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!
- *
- * In either case we use just K, M, G as suffix, and not Ki,
- * Mi, Gi or so (as IEC would suggest). That's because that's
- * frickin' ugly. But this means you really need to make sure
- * to document which base you are parsing when you use this
- * call. */
-
- struct table {
- const char *suffix;
- unsigned long long factor;
- };
-
- static const struct table iec[] = {
- { "E", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
- { "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
- { "T", 1024ULL*1024ULL*1024ULL*1024ULL },
- { "G", 1024ULL*1024ULL*1024ULL },
- { "M", 1024ULL*1024ULL },
- { "K", 1024ULL },
- { "B", 1ULL },
- { "", 1ULL },
- };
-
- static const struct table si[] = {
- { "E", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL*1000ULL },
- { "P", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL },
- { "T", 1000ULL*1000ULL*1000ULL*1000ULL },
- { "G", 1000ULL*1000ULL*1000ULL },
- { "M", 1000ULL*1000ULL },
- { "K", 1000ULL },
- { "B", 1ULL },
- { "", 1ULL },
- };
-
- const struct table *table;
- const char *p;
- unsigned long long r = 0;
- unsigned n_entries, start_pos = 0;
-
- assert(t);
- assert(base == 1000 || base == 1024);
- assert(size);
-
- if (base == 1000) {
- table = si;
- n_entries = ELEMENTSOF(si);
- } else {
- table = iec;
- n_entries = ELEMENTSOF(iec);
- }
-
- p = t;
- do {
- unsigned long long l, tmp;
- double frac = 0;
- char *e;
- unsigned i;
-
- p += strspn(p, WHITESPACE);
- if (*p == '-')
- return -ERANGE;
-
- errno = 0;
- l = strtoull(p, &e, 10);
- if (errno > 0)
- return -errno;
- if (e == p)
- return -EINVAL;
-
- if (*e == '.') {
- e++;
-
- /* strtoull() itself would accept space/+/- */
- if (*e >= '0' && *e <= '9') {
- unsigned long long l2;
- char *e2;
-
- l2 = strtoull(e, &e2, 10);
- if (errno > 0)
- return -errno;
-
- /* Ignore failure. E.g. 10.M is valid */
- frac = l2;
- for (; e < e2; e++)
- frac /= 10;
- }
- }
-
- e += strspn(e, WHITESPACE);
-
- for (i = start_pos; i < n_entries; i++)
- if (startswith(e, table[i].suffix))
- break;
-
- if (i >= n_entries)
- return -EINVAL;
-
- if (l + (frac > 0) > ULLONG_MAX / table[i].factor)
- return -ERANGE;
-
- tmp = l * table[i].factor + (unsigned long long) (frac * table[i].factor);
- if (tmp > ULLONG_MAX - r)
- return -ERANGE;
-
- r += tmp;
- if ((unsigned long long) (uint64_t) r != r)
- return -ERANGE;
-
- p = e + strlen(table[i].suffix);
-
- start_pos = i + 1;
-
- } while (*p);
-
- *size = r;
-
- return 0;
-}
-
-bool is_device_path(const char *path) {
-
- /* Returns true on paths that refer to a device, either in
- * sysfs or in /dev */
-
- return
- path_startswith(path, "/dev/") ||
- path_startswith(path, "/sys/");
-}
-
-int dir_is_empty(const char *path) {
- _cleanup_closedir_ DIR *d;
-
- d = opendir(path);
- if (!d)
- return -errno;
-
- for (;;) {
- struct dirent *de;
-
- errno = 0;
- de = readdir(d);
- if (!de && errno != 0)
- return -errno;
-
- if (!de)
- return 1;
-
- if (!hidden_file(de->d_name))
- return 0;
- }
-}
-
-char* dirname_malloc(const char *path) {
- char *d, *dir, *dir2;
-
- d = strdup(path);
- if (!d)
- return NULL;
- dir = dirname(d);
- assert(dir);
-
- if (dir != d) {
- dir2 = strdup(dir);
- free(d);
- return dir2;
- }
-
- return dir;
-}
-
-void rename_process(const char name[8]) {
- assert(name);
-
- /* This is a like a poor man's setproctitle(). It changes the
- * comm field, argv[0], and also the glibc's internally used
- * name of the process. For the first one a limit of 16 chars
- * applies, to the second one usually one of 10 (i.e. length
- * of "/sbin/init"), to the third one one of 7 (i.e. length of
- * "systemd"). If you pass a longer string it will be
- * truncated */
-
- prctl(PR_SET_NAME, name);
-
- if (program_invocation_name)
- strncpy(program_invocation_name, name, strlen(program_invocation_name));
-
- if (saved_argc > 0) {
- int i;
-
- if (saved_argv[0])
- strncpy(saved_argv[0], name, strlen(saved_argv[0]));
-
- for (i = 1; i < saved_argc; i++) {
- if (!saved_argv[i])
- break;
-
- memzero(saved_argv[i], strlen(saved_argv[i]));
- }
- }
-}
-
-char *lookup_uid(uid_t uid) {
- long bufsize;
- char *name;
- _cleanup_free_ char *buf = NULL;
- struct passwd pwbuf, *pw = NULL;
-
- /* Shortcut things to avoid NSS lookups */
- if (uid == 0)
- return strdup("root");
-
- bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
- if (bufsize <= 0)
- bufsize = 4096;
-
- buf = malloc(bufsize);
- if (!buf)
- return NULL;
-
- if (getpwuid_r(uid, &pwbuf, buf, bufsize, &pw) == 0 && pw)
- return strdup(pw->pw_name);
-
- if (asprintf(&name, UID_FMT, uid) < 0)
- return NULL;
-
- return name;
-}
-
-char* getlogname_malloc(void) {
- uid_t uid;
- struct stat st;
-
- if (isatty(STDIN_FILENO) && fstat(STDIN_FILENO, &st) >= 0)
- uid = st.st_uid;
- else
- uid = getuid();
-
- return lookup_uid(uid);
-}
-
-char *getusername_malloc(void) {
- const char *e;
-
- e = getenv("USER");
- if (e)
- return strdup(e);
-
- return lookup_uid(getuid());
-}
-
-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 fd_is_temporary_fs(int fd) {
- struct statfs s;
-
- if (fstatfs(fd, &s) < 0)
- return -errno;
-
- return is_temporary_fs(&s);
-}
-
-int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
- assert(path);
-
- /* Under the assumption that we are running privileged we
- * first change the access mode and only then hand out
- * ownership to avoid a window where access is too open. */
-
- if (mode != MODE_INVALID)
- if (chmod(path, mode) < 0)
- return -errno;
-
- if (uid != UID_INVALID || gid != GID_INVALID)
- if (chown(path, uid, gid) < 0)
- return -errno;
-
- return 0;
-}
-
-int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid) {
- assert(fd >= 0);
-
- /* Under the assumption that we are running privileged we
- * first change the access mode and only then hand out
- * ownership to avoid a window where access is too open. */
-
- if (mode != MODE_INVALID)
- if (fchmod(fd, mode) < 0)
- return -errno;
-
- if (uid != UID_INVALID || gid != GID_INVALID)
- if (fchown(fd, uid, gid) < 0)
- return -errno;
-
- return 0;
-}
-
-cpu_set_t* cpu_set_malloc(unsigned *ncpus) {
- cpu_set_t *r;
- unsigned n = 1024;
-
- /* Allocates the cpuset in the right size */
-
- for (;;) {
- if (!(r = CPU_ALLOC(n)))
- return NULL;
-
- if (sched_getaffinity(0, CPU_ALLOC_SIZE(n), r) >= 0) {
- CPU_ZERO_S(CPU_ALLOC_SIZE(n), r);
-
- if (ncpus)
- *ncpus = n;
-
- return r;
- }
-
- CPU_FREE(r);
-
- if (errno != EINVAL)
- return NULL;
-
- n *= 2;
- }
-}
-
-int files_same(const char *filea, const char *fileb) {
- struct stat a, b;
-
- if (stat(filea, &a) < 0)
- return -errno;
-
- if (stat(fileb, &b) < 0)
- return -errno;
-
- return a.st_dev == b.st_dev &&
- a.st_ino == b.st_ino;
-}
-
-int running_in_chroot(void) {
- int ret;
-
- ret = files_same("/proc/1/root", "/");
- if (ret < 0)
- return ret;
-
- return ret == 0;
-}
-
-static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
- size_t x;
- char *r;
-
- assert(s);
- assert(percent <= 100);
- assert(new_length >= 3);
-
- if (old_length <= 3 || old_length <= new_length)
- return strndup(s, old_length);
-
- r = new0(char, new_length+1);
- if (!r)
- return NULL;
-
- x = (new_length * percent) / 100;
-
- if (x > new_length - 3)
- x = new_length - 3;
-
- memcpy(r, s, x);
- r[x] = '.';
- r[x+1] = '.';
- r[x+2] = '.';
- memcpy(r + x + 3,
- s + old_length - (new_length - x - 3),
- new_length - x - 3);
-
- return r;
-}
-
-char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
- size_t x;
- char *e;
- const char *i, *j;
- unsigned k, len, len2;
-
- assert(s);
- assert(percent <= 100);
- assert(new_length >= 3);
-
- /* if no multibyte characters use ascii_ellipsize_mem for speed */
- if (ascii_is_valid(s))
- return ascii_ellipsize_mem(s, old_length, new_length, percent);
-
- if (old_length <= 3 || old_length <= new_length)
- return strndup(s, old_length);
-
- x = (new_length * percent) / 100;
-
- if (x > new_length - 3)
- x = new_length - 3;
-
- k = 0;
- for (i = s; k < x && i < s + old_length; i = utf8_next_char(i)) {
- int c;
-
- c = utf8_encoded_to_unichar(i);
- if (c < 0)
- return NULL;
- k += unichar_iswide(c) ? 2 : 1;
- }
-
- if (k > x) /* last character was wide and went over quota */
- x ++;
-
- for (j = s + old_length; k < new_length && j > i; ) {
- int c;
-
- j = utf8_prev_char(j);
- c = utf8_encoded_to_unichar(j);
- if (c < 0)
- return NULL;
- k += unichar_iswide(c) ? 2 : 1;
- }
- assert(i <= j);
-
- /* we don't actually need to ellipsize */
- if (i == j)
- return memdup(s, old_length + 1);
-
- /* make space for ellipsis */
- j = utf8_next_char(j);
-
- len = i - s;
- len2 = s + old_length - j;
- e = new(char, len + 3 + len2 + 1);
- if (!e)
- return NULL;
-
- /*
- printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n",
- old_length, new_length, x, len, len2, k);
- */
-
- memcpy(e, s, len);
- e[len] = 0xe2; /* tri-dot ellipsis: … */
- e[len + 1] = 0x80;
- e[len + 2] = 0xa6;
-
- memcpy(e + len + 3, j, len2 + 1);
-
- return e;
-}
-
-char *ellipsize(const char *s, size_t length, unsigned percent) {
- return ellipsize_mem(s, strlen(s), length, percent);
-}
-
-int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) {
- _cleanup_close_ int fd;
- int r;
-
- assert(path);
-
- if (parents)
- mkdir_parents(path, 0755);
-
- fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode > 0 ? mode : 0644);
- if (fd < 0)
- return -errno;
-
- if (mode > 0) {
- r = fchmod(fd, mode);
- if (r < 0)
- return -errno;
- }
-
- if (uid != UID_INVALID || gid != GID_INVALID) {
- r = fchown(fd, uid, gid);
- if (r < 0)
- return -errno;
- }
-
- if (stamp != USEC_INFINITY) {
- struct timespec ts[2];
-
- timespec_store(&ts[0], stamp);
- ts[1] = ts[0];
- r = futimens(fd, ts);
- } else
- r = futimens(fd, NULL);
- if (r < 0)
- return -errno;
-
- return 0;
-}
-
-int touch(const char *path) {
- return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, 0);
-}
-
-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.
- *
- * DON'T USE THIS FOR NEW CODE ANYMORE!*/
-
- l = strlen(s);
- if (l < 2)
- return strdup(s);
-
- if (strchr(quotes, s[0]) && s[l-1] == s[0])
- return strndup(s+1, l-2);
-
- return strdup(s);
-}
-
-noreturn void freeze(void) {
-
- /* Make sure nobody waits for us on a socket anymore */
- close_all_fds(NULL, 0);
-
- sync();
-
- for (;;)
- pause();
-}
-
-bool null_or_empty(struct stat *st) {
- assert(st);
-
- if (S_ISREG(st->st_mode) && st->st_size <= 0)
- return true;
-
- if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode))
- return true;
-
- return false;
-}
-
-int null_or_empty_path(const char *fn) {
- struct stat st;
-
- assert(fn);
-
- if (stat(fn, &st) < 0)
- return -errno;
-
- return null_or_empty(&st);
-}
-
-int null_or_empty_fd(int fd) {
- struct stat st;
-
- assert(fd >= 0);
-
- if (fstat(fd, &st) < 0)
- return -errno;
-
- return null_or_empty(&st);
-}
-
-DIR *xopendirat(int fd, const char *name, int flags) {
- int nfd;
- DIR *d;
-
- assert(!(flags & O_CREAT));
-
- nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags, 0);
- if (nfd < 0)
- return NULL;
-
- d = fdopendir(nfd);
- if (!d) {
- safe_close(nfd);
- return NULL;
- }
-
- return d;
-}
-
-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, QUOTES);
- if (!u)
- return NULL;
-
- enc_len = strlen(u) * 4 + 1;
- t = new(char, enc_len);
- if (!t)
- return NULL;
-
- if (encode_devnode_name(u, t, enc_len) < 0)
- return NULL;
-
- return strjoin("/dev/disk/by-", by, "/", t, NULL);
-}
-
-char *fstab_node_to_udev_node(const char *p) {
- assert(p);
-
- if (startswith(p, "LABEL="))
- return tag_to_udev_node(p+6, "label");
-
- if (startswith(p, "UUID="))
- return tag_to_udev_node(p+5, "uuid");
-
- if (startswith(p, "PARTUUID="))
- return tag_to_udev_node(p+9, "partuuid");
-
- if (startswith(p, "PARTLABEL="))
- return tag_to_udev_node(p+10, "partlabel");
-
- return strdup(p);
-}
-
-bool dirent_is_file(const struct dirent *de) {
- assert(de);
-
- if (hidden_file(de->d_name))
- return false;
-
- if (de->d_type != DT_REG &&
- de->d_type != DT_LNK &&
- de->d_type != DT_UNKNOWN)
- return false;
-
- return true;
-}
-
-bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) {
- assert(de);
-
- if (de->d_type != DT_REG &&
- de->d_type != DT_LNK &&
- de->d_type != DT_UNKNOWN)
- return false;
-
- if (hidden_file_allow_backup(de->d_name))
- return false;
-
- return endswith(de->d_name, suffix);
-}
-
static int do_execute(char **directories, usec_t timeout, char *argv[]) {
_cleanup_hashmap_free_free_ Hashmap *pids = NULL;
_cleanup_set_free_free_ Set *seen = NULL;
@@ -2983,7 +161,7 @@ static int do_execute(char **directories, usec_t timeout, char *argv[]) {
log_debug("Spawned %s as " PID_FMT ".", path, pid);
- r = hashmap_put(pids, UINT_TO_PTR(pid), path);
+ r = hashmap_put(pids, PID_TO_PTR(pid), path);
if (r < 0)
return log_oom();
path = NULL;
@@ -3001,10 +179,10 @@ static int do_execute(char **directories, usec_t timeout, char *argv[]) {
_cleanup_free_ char *path = NULL;
pid_t pid;
- pid = PTR_TO_UINT(hashmap_first_key(pids));
+ pid = PTR_TO_PID(hashmap_first_key(pids));
assert(pid > 0);
- path = hashmap_remove(pids, UINT_TO_PTR(pid));
+ path = hashmap_remove(pids, PID_TO_PTR(pid));
assert(path);
wait_for_terminate_and_warn(path, pid, true);
@@ -3042,189 +220,10 @@ void execute_directories(const char* const* directories, usec_t timeout, char *a
wait_for_terminate_and_warn(name, executor_pid, true);
}
-bool nulstr_contains(const char*nulstr, const char *needle) {
- const char *i;
-
- if (!nulstr)
- return false;
-
- NULSTR_FOREACH(i, nulstr)
- if (streq(i, needle))
- return true;
-
- return false;
-}
-
bool plymouth_running(void) {
return access("/run/plymouth/pid", F_OK) >= 0;
}
-char* strshorten(char *s, size_t l) {
- assert(s);
-
- if (l < strlen(s))
- s[l] = 0;
-
- return s;
-}
-
-int pipe_eof(int fd) {
- struct pollfd pollfd = {
- .fd = fd,
- .events = POLLIN|POLLHUP,
- };
-
- int r;
-
- r = poll(&pollfd, 1, 0);
- if (r < 0)
- return -errno;
-
- if (r == 0)
- return 0;
-
- return pollfd.revents & POLLHUP;
-}
-
-int fd_wait_for_event(int fd, int event, usec_t t) {
-
- struct pollfd pollfd = {
- .fd = fd,
- .events = event,
- };
-
- struct timespec ts;
- int r;
-
- r = ppoll(&pollfd, 1, t == USEC_INFINITY ? NULL : timespec_store(&ts, t), NULL);
- if (r < 0)
- return -errno;
-
- if (r == 0)
- return 0;
-
- return pollfd.revents;
-}
-
-int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
- FILE *f;
- char *t;
- int r, fd;
-
- assert(path);
- assert(_f);
- assert(_temp_path);
-
- r = tempfn_xxxxxx(path, NULL, &t);
- if (r < 0)
- return r;
-
- fd = mkostemp_safe(t, O_WRONLY|O_CLOEXEC);
- if (fd < 0) {
- free(t);
- return -errno;
- }
-
- f = fdopen(fd, "we");
- if (!f) {
- unlink_noerrno(t);
- free(t);
- safe_close(fd);
- return -errno;
- }
-
- *_f = f;
- *_temp_path = t;
-
- return 0;
-}
-
-int symlink_atomic(const char *from, const char *to) {
- _cleanup_free_ char *t = NULL;
- int r;
-
- assert(from);
- assert(to);
-
- r = tempfn_random(to, NULL, &t);
- if (r < 0)
- return r;
-
- if (symlink(from, t) < 0)
- return -errno;
-
- if (rename(t, to) < 0) {
- unlink_noerrno(t);
- return -errno;
- }
-
- return 0;
-}
-
-int symlink_idempotent(const char *from, const char *to) {
- _cleanup_free_ char *p = NULL;
- int r;
-
- assert(from);
- assert(to);
-
- if (symlink(from, to) < 0) {
- if (errno != EEXIST)
- return -errno;
-
- r = readlink_malloc(to, &p);
- if (r < 0)
- return r;
-
- if (!streq(p, from))
- return -EINVAL;
- }
-
- return 0;
-}
-
-int mknod_atomic(const char *path, mode_t mode, dev_t dev) {
- _cleanup_free_ char *t = NULL;
- int r;
-
- assert(path);
-
- r = tempfn_random(path, NULL, &t);
- if (r < 0)
- return r;
-
- if (mknod(t, mode, dev) < 0)
- return -errno;
-
- if (rename(t, path) < 0) {
- unlink_noerrno(t);
- return -errno;
- }
-
- return 0;
-}
-
-int mkfifo_atomic(const char *path, mode_t mode) {
- _cleanup_free_ char *t = NULL;
- int r;
-
- assert(path);
-
- r = tempfn_random(path, NULL, &t);
- if (r < 0)
- return r;
-
- if (mkfifo(t, mode) < 0)
- return -errno;
-
- if (rename(t, path) < 0) {
- unlink_noerrno(t);
- return -errno;
- }
-
- return 0;
-}
-
bool display_is_local(const char *display) {
assert(display);
@@ -3259,368 +258,6 @@ int socket_from_display(const char *display, char **path) {
return 0;
}
-int get_user_creds(
- const char **username,
- uid_t *uid, gid_t *gid,
- const char **home,
- const char **shell) {
-
- struct passwd *p;
- uid_t u;
-
- assert(username);
- assert(*username);
-
- /* We enforce some special rules for uid=0: in order to avoid
- * NSS lookups for root we hardcode its data. */
-
- if (streq(*username, "root") || streq(*username, "0")) {
- *username = "root";
-
- if (uid)
- *uid = 0;
-
- if (gid)
- *gid = 0;
-
- if (home)
- *home = "/root";
-
- if (shell)
- *shell = "/bin/sh";
-
- return 0;
- }
-
- if (parse_uid(*username, &u) >= 0) {
- errno = 0;
- p = getpwuid(u);
-
- /* If there are multiple users with the same id, make
- * sure to leave $USER to the configured value instead
- * of the first occurrence in the database. However if
- * the uid was configured by a numeric uid, then let's
- * pick the real username from /etc/passwd. */
- if (p)
- *username = p->pw_name;
- } else {
- errno = 0;
- p = getpwnam(*username);
- }
-
- if (!p)
- return errno > 0 ? -errno : -ESRCH;
-
- if (uid)
- *uid = p->pw_uid;
-
- if (gid)
- *gid = p->pw_gid;
-
- if (home)
- *home = p->pw_dir;
-
- if (shell)
- *shell = p->pw_shell;
-
- return 0;
-}
-
-char* uid_to_name(uid_t uid) {
- struct passwd *p;
- char *r;
-
- if (uid == 0)
- return strdup("root");
-
- p = getpwuid(uid);
- if (p)
- return strdup(p->pw_name);
-
- if (asprintf(&r, UID_FMT, uid) < 0)
- return NULL;
-
- return r;
-}
-
-char* gid_to_name(gid_t gid) {
- struct group *p;
- char *r;
-
- if (gid == 0)
- return strdup("root");
-
- p = getgrgid(gid);
- if (p)
- return strdup(p->gr_name);
-
- if (asprintf(&r, GID_FMT, gid) < 0)
- return NULL;
-
- return r;
-}
-
-int get_group_creds(const char **groupname, gid_t *gid) {
- struct group *g;
- gid_t id;
-
- assert(groupname);
-
- /* We enforce some special rules for gid=0: in order to avoid
- * NSS lookups for root we hardcode its data. */
-
- if (streq(*groupname, "root") || streq(*groupname, "0")) {
- *groupname = "root";
-
- if (gid)
- *gid = 0;
-
- return 0;
- }
-
- if (parse_gid(*groupname, &id) >= 0) {
- errno = 0;
- g = getgrgid(id);
-
- if (g)
- *groupname = g->gr_name;
- } else {
- errno = 0;
- g = getgrnam(*groupname);
- }
-
- if (!g)
- return errno > 0 ? -errno : -ESRCH;
-
- if (gid)
- *gid = g->gr_gid;
-
- return 0;
-}
-
-int in_gid(gid_t gid) {
- gid_t *gids;
- int ngroups_max, r, i;
-
- if (getgid() == gid)
- return 1;
-
- if (getegid() == gid)
- return 1;
-
- ngroups_max = sysconf(_SC_NGROUPS_MAX);
- assert(ngroups_max > 0);
-
- gids = alloca(sizeof(gid_t) * ngroups_max);
-
- r = getgroups(ngroups_max, gids);
- if (r < 0)
- return -errno;
-
- for (i = 0; i < r; i++)
- if (gids[i] == gid)
- return 1;
-
- return 0;
-}
-
-int in_group(const char *name) {
- int r;
- gid_t gid;
-
- r = get_group_creds(&name, &gid);
- if (r < 0)
- return r;
-
- return in_gid(gid);
-}
-
-int glob_exists(const char *path) {
- _cleanup_globfree_ glob_t g = {};
- int k;
-
- assert(path);
-
- errno = 0;
- k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
-
- if (k == GLOB_NOMATCH)
- return 0;
- else if (k == GLOB_NOSPACE)
- return -ENOMEM;
- else if (k == 0)
- return !strv_isempty(g.gl_pathv);
- else
- return errno ? -errno : -EIO;
-}
-
-int glob_extend(char ***strv, const char *path) {
- _cleanup_globfree_ glob_t g = {};
- int k;
- char **p;
-
- errno = 0;
- k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
-
- if (k == GLOB_NOMATCH)
- return -ENOENT;
- else if (k == GLOB_NOSPACE)
- return -ENOMEM;
- else if (k != 0 || strv_isempty(g.gl_pathv))
- return errno ? -errno : -EIO;
-
- STRV_FOREACH(p, g.gl_pathv) {
- k = strv_extend(strv, *p);
- if (k < 0)
- break;
- }
-
- return k;
-}
-
-int dirent_ensure_type(DIR *d, struct dirent *de) {
- struct stat st;
-
- assert(d);
- assert(de);
-
- if (de->d_type != DT_UNKNOWN)
- return 0;
-
- if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0)
- return -errno;
-
- de->d_type =
- S_ISREG(st.st_mode) ? DT_REG :
- S_ISDIR(st.st_mode) ? DT_DIR :
- S_ISLNK(st.st_mode) ? DT_LNK :
- S_ISFIFO(st.st_mode) ? DT_FIFO :
- S_ISSOCK(st.st_mode) ? DT_SOCK :
- S_ISCHR(st.st_mode) ? DT_CHR :
- S_ISBLK(st.st_mode) ? DT_BLK :
- DT_UNKNOWN;
-
- return 0;
-}
-
-int get_files_in_directory(const char *path, char ***list) {
- _cleanup_closedir_ DIR *d = NULL;
- size_t bufsize = 0, n = 0;
- _cleanup_strv_free_ char **l = NULL;
-
- assert(path);
-
- /* Returns all files in a directory in *list, and the number
- * of files as return value. If list is NULL returns only the
- * number. */
-
- d = opendir(path);
- if (!d)
- return -errno;
-
- for (;;) {
- struct dirent *de;
-
- errno = 0;
- de = readdir(d);
- if (!de && errno != 0)
- return -errno;
- if (!de)
- break;
-
- dirent_ensure_type(d, de);
-
- if (!dirent_is_file(de))
- continue;
-
- if (list) {
- /* one extra slot is needed for the terminating NULL */
- if (!GREEDY_REALLOC(l, bufsize, n + 2))
- return -ENOMEM;
-
- l[n] = strdup(de->d_name);
- if (!l[n])
- return -ENOMEM;
-
- l[++n] = NULL;
- } else
- n++;
- }
-
- if (list) {
- *list = l;
- l = NULL; /* avoid freeing */
- }
-
- return n;
-}
-
-char *strjoin(const char *x, ...) {
- va_list ap;
- size_t l;
- char *r, *p;
-
- va_start(ap, x);
-
- if (x) {
- l = strlen(x);
-
- for (;;) {
- const char *t;
- size_t n;
-
- t = va_arg(ap, const char *);
- if (!t)
- break;
-
- n = strlen(t);
- if (n > ((size_t) -1) - l) {
- va_end(ap);
- return NULL;
- }
-
- l += n;
- }
- } else
- l = 0;
-
- va_end(ap);
-
- r = new(char, l+1);
- if (!r)
- return NULL;
-
- if (x) {
- p = stpcpy(r, x);
-
- va_start(ap, x);
-
- for (;;) {
- const char *t;
-
- t = va_arg(ap, const char *);
- if (!t)
- break;
-
- p = stpcpy(p, t);
- }
-
- va_end(ap);
- } else
- r[0] = 0;
-
- return r;
-}
-
-bool is_main_thread(void) {
- static thread_local int cached = 0;
-
- if (_unlikely_(cached == 0))
- cached = getpid() == gettid() ? 1 : -1;
-
- return cached > 0;
-}
-
int block_get_whole_disk(dev_t d, dev_t *ret) {
char *p, *s;
int r;
@@ -3681,104 +318,6 @@ int block_get_whole_disk(dev_t d, dev_t *ret) {
return -ENOENT;
}
-static const char *const ioprio_class_table[] = {
- [IOPRIO_CLASS_NONE] = "none",
- [IOPRIO_CLASS_RT] = "realtime",
- [IOPRIO_CLASS_BE] = "best-effort",
- [IOPRIO_CLASS_IDLE] = "idle"
-};
-
-DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ioprio_class, int, INT_MAX);
-
-static const char *const sigchld_code_table[] = {
- [CLD_EXITED] = "exited",
- [CLD_KILLED] = "killed",
- [CLD_DUMPED] = "dumped",
- [CLD_TRAPPED] = "trapped",
- [CLD_STOPPED] = "stopped",
- [CLD_CONTINUED] = "continued",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(sigchld_code, int);
-
-static const char *const log_facility_unshifted_table[LOG_NFACILITIES] = {
- [LOG_FAC(LOG_KERN)] = "kern",
- [LOG_FAC(LOG_USER)] = "user",
- [LOG_FAC(LOG_MAIL)] = "mail",
- [LOG_FAC(LOG_DAEMON)] = "daemon",
- [LOG_FAC(LOG_AUTH)] = "auth",
- [LOG_FAC(LOG_SYSLOG)] = "syslog",
- [LOG_FAC(LOG_LPR)] = "lpr",
- [LOG_FAC(LOG_NEWS)] = "news",
- [LOG_FAC(LOG_UUCP)] = "uucp",
- [LOG_FAC(LOG_CRON)] = "cron",
- [LOG_FAC(LOG_AUTHPRIV)] = "authpriv",
- [LOG_FAC(LOG_FTP)] = "ftp",
- [LOG_FAC(LOG_LOCAL0)] = "local0",
- [LOG_FAC(LOG_LOCAL1)] = "local1",
- [LOG_FAC(LOG_LOCAL2)] = "local2",
- [LOG_FAC(LOG_LOCAL3)] = "local3",
- [LOG_FAC(LOG_LOCAL4)] = "local4",
- [LOG_FAC(LOG_LOCAL5)] = "local5",
- [LOG_FAC(LOG_LOCAL6)] = "local6",
- [LOG_FAC(LOG_LOCAL7)] = "local7"
-};
-
-DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(log_facility_unshifted, int, LOG_FAC(~0));
-
-static const char *const log_level_table[] = {
- [LOG_EMERG] = "emerg",
- [LOG_ALERT] = "alert",
- [LOG_CRIT] = "crit",
- [LOG_ERR] = "err",
- [LOG_WARNING] = "warning",
- [LOG_NOTICE] = "notice",
- [LOG_INFO] = "info",
- [LOG_DEBUG] = "debug"
-};
-
-DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(log_level, int, LOG_DEBUG);
-
-static const char* const sched_policy_table[] = {
- [SCHED_OTHER] = "other",
- [SCHED_BATCH] = "batch",
- [SCHED_IDLE] = "idle",
- [SCHED_FIFO] = "fifo",
- [SCHED_RR] = "rr"
-};
-
-DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(sched_policy, int, INT_MAX);
-
-static const char* const rlimit_table[_RLIMIT_MAX] = {
- [RLIMIT_CPU] = "LimitCPU",
- [RLIMIT_FSIZE] = "LimitFSIZE",
- [RLIMIT_DATA] = "LimitDATA",
- [RLIMIT_STACK] = "LimitSTACK",
- [RLIMIT_CORE] = "LimitCORE",
- [RLIMIT_RSS] = "LimitRSS",
- [RLIMIT_NOFILE] = "LimitNOFILE",
- [RLIMIT_AS] = "LimitAS",
- [RLIMIT_NPROC] = "LimitNPROC",
- [RLIMIT_MEMLOCK] = "LimitMEMLOCK",
- [RLIMIT_LOCKS] = "LimitLOCKS",
- [RLIMIT_SIGPENDING] = "LimitSIGPENDING",
- [RLIMIT_MSGQUEUE] = "LimitMSGQUEUE",
- [RLIMIT_NICE] = "LimitNICE",
- [RLIMIT_RTPRIO] = "LimitRTPRIO",
- [RLIMIT_RTTIME] = "LimitRTTIME"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(rlimit, int);
-
-static const char* const ip_tos_table[] = {
- [IPTOS_LOWDELAY] = "low-delay",
- [IPTOS_THROUGHPUT] = "throughput",
- [IPTOS_RELIABILITY] = "reliability",
- [IPTOS_LOWCOST] = "low-cost",
-};
-
-DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff);
-
bool kexec_loaded(void) {
bool loaded = false;
char *s;
@@ -3809,93 +348,6 @@ int prot_from_flags(int flags) {
}
}
-char *format_bytes(char *buf, size_t l, uint64_t t) {
- unsigned i;
-
- static const struct {
- const char *suffix;
- uint64_t factor;
- } table[] = {
- { "E", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
- { "P", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
- { "T", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
- { "G", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
- { "M", UINT64_C(1024)*UINT64_C(1024) },
- { "K", UINT64_C(1024) },
- };
-
- if (t == (uint64_t) -1)
- return NULL;
-
- for (i = 0; i < ELEMENTSOF(table); i++) {
-
- if (t >= table[i].factor) {
- snprintf(buf, l,
- "%" PRIu64 ".%" PRIu64 "%s",
- t / table[i].factor,
- ((t*UINT64_C(10)) / table[i].factor) % UINT64_C(10),
- table[i].suffix);
-
- goto finish;
- }
- }
-
- snprintf(buf, l, "%" PRIu64 "B", t);
-
-finish:
- buf[l-1] = 0;
- return buf;
-
-}
-
-void* memdup(const void *p, size_t l) {
- void *r;
-
- assert(p);
-
- r = malloc(l);
- if (!r)
- return NULL;
-
- memcpy(r, p, l);
- return r;
-}
-
-int fd_inc_sndbuf(int fd, size_t n) {
- int r, value;
- socklen_t l = sizeof(value);
-
- r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l);
- if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2)
- return 0;
-
- /* If we have the privileges we will ignore the kernel limit. */
-
- value = (int) n;
- if (setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, &value, sizeof(value)) < 0)
- if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0)
- return -errno;
-
- return 1;
-}
-
-int fd_inc_rcvbuf(int fd, size_t n) {
- int r, value;
- socklen_t l = sizeof(value);
-
- r = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, &l);
- if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2)
- return 0;
-
- /* If we have the privileges we will ignore the kernel limit. */
-
- value = (int) n;
- if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0)
- if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) < 0)
- return -errno;
- return 1;
-}
-
int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...) {
bool stdout_is_tty, stderr_is_tty;
pid_t parent_pid, agent_pid;
@@ -3998,82 +450,6 @@ int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *pa
_exit(EXIT_FAILURE);
}
-int setrlimit_closest(int resource, const struct rlimit *rlim) {
- struct rlimit highest, fixed;
-
- assert(rlim);
-
- if (setrlimit(resource, rlim) >= 0)
- return 0;
-
- if (errno != EPERM)
- return -errno;
-
- /* So we failed to set the desired setrlimit, then let's try
- * to get as close as we can */
- assert_se(getrlimit(resource, &highest) == 0);
-
- fixed.rlim_cur = MIN(rlim->rlim_cur, highest.rlim_max);
- fixed.rlim_max = MIN(rlim->rlim_max, highest.rlim_max);
-
- if (setrlimit(resource, &fixed) < 0)
- return -errno;
-
- return 0;
-}
-
-bool http_etag_is_valid(const char *etag) {
- if (isempty(etag))
- return false;
-
- if (!endswith(etag, "\""))
- return false;
-
- if (!startswith(etag, "\"") && !startswith(etag, "W/\""))
- return false;
-
- return true;
-}
-
-bool http_url_is_valid(const char *url) {
- const char *p;
-
- if (isempty(url))
- return false;
-
- p = startswith(url, "http://");
- if (!p)
- p = startswith(url, "https://");
- if (!p)
- return false;
-
- if (isempty(p))
- return false;
-
- return ascii_is_valid(p);
-}
-
-bool documentation_url_is_valid(const char *url) {
- const char *p;
-
- if (isempty(url))
- return false;
-
- if (http_url_is_valid(url))
- return true;
-
- p = startswith(url, "file:/");
- if (!p)
- p = startswith(url, "info:");
- if (!p)
- p = startswith(url, "man:");
-
- if (isempty(p))
- return false;
-
- return ascii_is_valid(p);
-}
-
bool in_initrd(void) {
static int saved = -1;
struct statfs s;
@@ -4098,181 +474,6 @@ bool in_initrd(void) {
return saved;
}
-int get_home_dir(char **_h) {
- struct passwd *p;
- const char *e;
- char *h;
- uid_t u;
-
- assert(_h);
-
- /* Take the user specified one */
- e = secure_getenv("HOME");
- if (e && path_is_absolute(e)) {
- h = strdup(e);
- if (!h)
- return -ENOMEM;
-
- *_h = h;
- return 0;
- }
-
- /* Hardcode home directory for root to avoid NSS */
- u = getuid();
- if (u == 0) {
- h = strdup("/root");
- if (!h)
- return -ENOMEM;
-
- *_h = h;
- return 0;
- }
-
- /* Check the database... */
- errno = 0;
- p = getpwuid(u);
- if (!p)
- return errno > 0 ? -errno : -ESRCH;
-
- if (!path_is_absolute(p->pw_dir))
- return -EINVAL;
-
- h = strdup(p->pw_dir);
- if (!h)
- return -ENOMEM;
-
- *_h = h;
- return 0;
-}
-
-int get_shell(char **_s) {
- struct passwd *p;
- const char *e;
- char *s;
- uid_t u;
-
- assert(_s);
-
- /* Take the user specified one */
- e = getenv("SHELL");
- if (e) {
- s = strdup(e);
- if (!s)
- return -ENOMEM;
-
- *_s = s;
- return 0;
- }
-
- /* Hardcode home directory for root to avoid NSS */
- u = getuid();
- if (u == 0) {
- s = strdup("/bin/sh");
- if (!s)
- return -ENOMEM;
-
- *_s = s;
- return 0;
- }
-
- /* Check the database... */
- errno = 0;
- p = getpwuid(u);
- if (!p)
- return errno > 0 ? -errno : -ESRCH;
-
- if (!path_is_absolute(p->pw_shell))
- return -EINVAL;
-
- s = strdup(p->pw_shell);
- if (!s)
- return -ENOMEM;
-
- *_s = s;
- return 0;
-}
-
-bool filename_is_valid(const char *p) {
-
- if (isempty(p))
- return false;
-
- if (strchr(p, '/'))
- return false;
-
- if (streq(p, "."))
- return false;
-
- if (streq(p, ".."))
- return false;
-
- if (strlen(p) > FILENAME_MAX)
- return false;
-
- return true;
-}
-
-bool string_is_safe(const char *p) {
- const char *t;
-
- if (!p)
- return false;
-
- for (t = p; *t; t++) {
- if (*t > 0 && *t < ' ')
- return false;
-
- if (strchr("\\\"\'\x7f", *t))
- return false;
- }
-
- return true;
-}
-
-/**
- * Check if a string contains control characters. If 'ok' is non-NULL
- * it may be a string containing additional CCs to be considered OK.
- */
-bool string_has_cc(const char *p, const char *ok) {
- const char *t;
-
- assert(p);
-
- for (t = p; *t; t++) {
- if (ok && strchr(ok, *t))
- continue;
-
- if (*t > 0 && *t < ' ')
- return true;
-
- if (*t == 127)
- return true;
- }
-
- return false;
-}
-
-bool path_is_safe(const char *p) {
-
- if (isempty(p))
- return false;
-
- if (streq(p, "..") || startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../"))
- return false;
-
- if (strlen(p)+1 > PATH_MAX)
- return false;
-
- /* The following two checks are not really dangerous, but hey, they still are confusing */
- if (streq(p, ".") || startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./"))
- return false;
-
- if (strstr(p, "//"))
- return false;
-
- return true;
-}
-
/* hey glibc, APIs with callbacks without a user pointer are so useless */
void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
int (*compar) (const void *, const void *, void *), void *arg) {
@@ -4296,216 +497,6 @@ 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;
-
- if (cached_answer >= 0)
- goto out;
-
- if (!setlocale(LC_ALL, "")) {
- cached_answer = true;
- goto out;
- }
-
- set = nl_langinfo(CODESET);
- if (!set) {
- cached_answer = true;
- goto out;
- }
-
- if (streq(set, "UTF-8")) {
- cached_answer = true;
- goto out;
- }
-
- /* For LC_CTYPE=="C" return true, because CTYPE is effectly
- * unset and everything can do to UTF-8 nowadays. */
- set = setlocale(LC_CTYPE, NULL);
- if (!set) {
- cached_answer = true;
- goto out;
- }
-
- /* Check result, but ignore the result if C was set
- * explicitly. */
- cached_answer =
- STR_IN_SET(set, "C", "POSIX") &&
- !getenv("LC_ALL") &&
- !getenv("LC_CTYPE") &&
- !getenv("LANG");
-
-out:
- return (bool) cached_answer;
-}
-
-const char *draw_special_char(DrawSpecialChar ch) {
- static const char *draw_table[2][_DRAW_SPECIAL_CHAR_MAX] = {
-
- /* UTF-8 */ {
- [DRAW_TREE_VERTICAL] = "\342\224\202 ", /* │ */
- [DRAW_TREE_BRANCH] = "\342\224\234\342\224\200", /* ├─ */
- [DRAW_TREE_RIGHT] = "\342\224\224\342\224\200", /* └─ */
- [DRAW_TREE_SPACE] = " ", /* */
- [DRAW_TRIANGULAR_BULLET] = "\342\200\243", /* ‣ */
- [DRAW_BLACK_CIRCLE] = "\342\227\217", /* ● */
- [DRAW_ARROW] = "\342\206\222", /* → */
- [DRAW_DASH] = "\342\200\223", /* – */
- },
-
- /* ASCII fallback */ {
- [DRAW_TREE_VERTICAL] = "| ",
- [DRAW_TREE_BRANCH] = "|-",
- [DRAW_TREE_RIGHT] = "`-",
- [DRAW_TREE_SPACE] = " ",
- [DRAW_TRIANGULAR_BULLET] = ">",
- [DRAW_BLACK_CIRCLE] = "*",
- [DRAW_ARROW] = "->",
- [DRAW_DASH] = "-",
- }
- };
-
- return draw_table[!is_locale_utf8()][ch];
-}
-
-char *strreplace(const char *text, const char *old_string, const char *new_string) {
- const char *f;
- char *t, *r;
- size_t l, old_len, new_len;
-
- assert(text);
- assert(old_string);
- assert(new_string);
-
- old_len = strlen(old_string);
- new_len = strlen(new_string);
-
- l = strlen(text);
- r = new(char, l+1);
- if (!r)
- return NULL;
-
- f = text;
- t = r;
- while (*f) {
- char *a;
- size_t d, nl;
-
- if (!startswith(f, old_string)) {
- *(t++) = *(f++);
- continue;
- }
-
- d = t - r;
- nl = l - old_len + new_len;
- a = realloc(r, nl + 1);
- if (!a)
- goto oom;
-
- l = nl;
- r = a;
- t = r + d;
-
- t = stpcpy(t, new_string);
- f += old_len;
- }
-
- *t = 0;
- return r;
-
-oom:
- free(r);
- return NULL;
-}
-
-char *strip_tab_ansi(char **ibuf, size_t *_isz) {
- const char *i, *begin = NULL;
- enum {
- STATE_OTHER,
- STATE_ESCAPE,
- STATE_BRACKET
- } state = STATE_OTHER;
- char *obuf = NULL;
- size_t osz = 0, isz;
- FILE *f;
-
- assert(ibuf);
- assert(*ibuf);
-
- /* Strips ANSI color and replaces TABs by 8 spaces */
-
- isz = _isz ? *_isz : strlen(*ibuf);
-
- f = open_memstream(&obuf, &osz);
- if (!f)
- return NULL;
-
- for (i = *ibuf; i < *ibuf + isz + 1; i++) {
-
- switch (state) {
-
- case STATE_OTHER:
- if (i >= *ibuf + isz) /* EOT */
- break;
- else if (*i == '\x1B')
- state = STATE_ESCAPE;
- else if (*i == '\t')
- fputs(" ", f);
- else
- fputc(*i, f);
- break;
-
- case STATE_ESCAPE:
- if (i >= *ibuf + isz) { /* EOT */
- fputc('\x1B', f);
- break;
- } else if (*i == '[') {
- state = STATE_BRACKET;
- begin = i + 1;
- } else {
- fputc('\x1B', f);
- fputc(*i, f);
- state = STATE_OTHER;
- }
-
- break;
-
- case STATE_BRACKET:
-
- if (i >= *ibuf + isz || /* EOT */
- (!(*i >= '0' && *i <= '9') && *i != ';' && *i != 'm')) {
- fputc('\x1B', f);
- fputc('[', f);
- state = STATE_OTHER;
- i = begin-1;
- } else if (*i == 'm')
- state = STATE_OTHER;
- break;
- }
- }
-
- if (ferror(f)) {
- fclose(f);
- free(obuf);
- return NULL;
- }
-
- fclose(f);
-
- free(*ibuf);
- *ibuf = obuf;
-
- if (_isz)
- *_isz = osz;
-
- return obuf;
-}
-
int on_ac_power(void) {
bool found_offline = false, found_online = false;
_cleanup_closedir_ DIR *d = NULL;
@@ -4522,7 +513,7 @@ int on_ac_power(void) {
errno = 0;
de = readdir(d);
- if (!de && errno != 0)
+ if (!de && errno > 0)
return -errno;
if (!de)
@@ -4582,204 +573,6 @@ int on_ac_power(void) {
return found_online || !found_offline;
}
-static int search_and_fopen_internal(const char *path, const char *mode, const char *root, char **search, FILE **_f) {
- char **i;
-
- assert(path);
- assert(mode);
- assert(_f);
-
- if (!path_strv_resolve_uniq(search, root))
- return -ENOMEM;
-
- STRV_FOREACH(i, search) {
- _cleanup_free_ char *p = NULL;
- FILE *f;
-
- if (root)
- p = strjoin(root, *i, "/", path, NULL);
- else
- p = strjoin(*i, "/", path, NULL);
- if (!p)
- return -ENOMEM;
-
- f = fopen(p, mode);
- if (f) {
- *_f = f;
- return 0;
- }
-
- if (errno != ENOENT)
- return -errno;
- }
-
- return -ENOENT;
-}
-
-int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f) {
- _cleanup_strv_free_ char **copy = NULL;
-
- assert(path);
- assert(mode);
- assert(_f);
-
- if (path_is_absolute(path)) {
- FILE *f;
-
- f = fopen(path, mode);
- if (f) {
- *_f = f;
- return 0;
- }
-
- return -errno;
- }
-
- copy = strv_copy((char**) search);
- if (!copy)
- return -ENOMEM;
-
- return search_and_fopen_internal(path, mode, root, copy, _f);
-}
-
-int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f) {
- _cleanup_strv_free_ char **s = NULL;
-
- if (path_is_absolute(path)) {
- FILE *f;
-
- f = fopen(path, mode);
- if (f) {
- *_f = f;
- return 0;
- }
-
- return -errno;
- }
-
- s = strv_split_nulstr(search);
- if (!s)
- return -ENOMEM;
-
- return search_and_fopen_internal(path, mode, root, s, _f);
-}
-
-char *strextend(char **x, ...) {
- va_list ap;
- size_t f, l;
- char *r, *p;
-
- assert(x);
-
- l = f = *x ? strlen(*x) : 0;
-
- va_start(ap, x);
- for (;;) {
- const char *t;
- size_t n;
-
- t = va_arg(ap, const char *);
- if (!t)
- break;
-
- n = strlen(t);
- if (n > ((size_t) -1) - l) {
- va_end(ap);
- return NULL;
- }
-
- l += n;
- }
- va_end(ap);
-
- r = realloc(*x, l+1);
- if (!r)
- return NULL;
-
- p = r + f;
-
- va_start(ap, x);
- for (;;) {
- const char *t;
-
- t = va_arg(ap, const char *);
- if (!t)
- break;
-
- p = stpcpy(p, t);
- }
- va_end(ap);
-
- *p = 0;
- *x = r;
-
- return r + l;
-}
-
-char *strrep(const char *s, unsigned n) {
- size_t l;
- char *r, *p;
- unsigned i;
-
- assert(s);
-
- l = strlen(s);
- p = r = malloc(l * n + 1);
- if (!r)
- return NULL;
-
- for (i = 0; i < n; i++)
- p = stpcpy(p, s);
-
- *p = 0;
- return r;
-}
-
-void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size) {
- size_t a, newalloc;
- void *q;
-
- assert(p);
- assert(allocated);
-
- if (*allocated >= need)
- return *p;
-
- newalloc = MAX(need * 2, 64u / size);
- a = newalloc * size;
-
- /* check for overflows */
- if (a < size * need)
- return NULL;
-
- q = realloc(*p, a);
- if (!q)
- return NULL;
-
- *p = q;
- *allocated = newalloc;
- return q;
-}
-
-void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size) {
- size_t prev;
- uint8_t *q;
-
- assert(p);
- assert(allocated);
-
- prev = *allocated;
-
- q = greedy_realloc(p, allocated, need, size);
- if (!q)
- return NULL;
-
- if (*allocated > prev)
- memzero(q + prev * size, (*allocated - prev) * size);
-
- return q;
-}
-
bool id128_is_valid(const char *s) {
size_t i, l;
@@ -4821,151 +614,6 @@ bool id128_is_valid(const char *s) {
return true;
}
-int split_pair(const char *s, const char *sep, char **l, char **r) {
- char *x, *a, *b;
-
- assert(s);
- assert(sep);
- assert(l);
- assert(r);
-
- if (isempty(sep))
- return -EINVAL;
-
- x = strstr(s, sep);
- if (!x)
- return -EINVAL;
-
- a = strndup(s, x - s);
- if (!a)
- return -ENOMEM;
-
- b = strdup(x + strlen(sep));
- if (!b) {
- free(a);
- return -ENOMEM;
- }
-
- *l = a;
- *r = b;
-
- return 0;
-}
-
-int shall_restore_state(void) {
- _cleanup_free_ char *value = NULL;
- int r;
-
- r = get_proc_cmdline_key("systemd.restore_state=", &value);
- if (r < 0)
- return r;
- if (r == 0)
- return true;
-
- return parse_boolean(value) != 0;
-}
-
-int proc_cmdline(char **ret) {
- assert(ret);
-
- if (detect_container() > 0)
- return get_process_cmdline(1, 0, false, ret);
- else
- return read_one_line_file("/proc/cmdline", ret);
-}
-
-int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value)) {
- _cleanup_free_ char *line = NULL;
- const char *p;
- int r;
-
- assert(parse_item);
-
- r = proc_cmdline(&line);
- if (r < 0)
- return r;
-
- p = line;
- for (;;) {
- _cleanup_free_ char *word = NULL;
- char *value = NULL;
-
- r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
- if (r < 0)
- return r;
- if (r == 0)
- break;
-
- /* Filter out arguments that are intended only for the
- * initrd */
- if (!in_initrd() && startswith(word, "rd."))
- continue;
-
- value = strchr(word, '=');
- if (value)
- *(value++) = 0;
-
- r = parse_item(word, value);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-int get_proc_cmdline_key(const char *key, char **value) {
- _cleanup_free_ char *line = NULL, *ret = NULL;
- bool found = false;
- const char *p;
- int r;
-
- assert(key);
-
- r = proc_cmdline(&line);
- if (r < 0)
- return r;
-
- p = line;
- for (;;) {
- _cleanup_free_ char *word = NULL;
- const char *e;
-
- r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
- if (r < 0)
- return r;
- if (r == 0)
- break;
-
- /* Filter out arguments that are intended only for the
- * initrd */
- if (!in_initrd() && startswith(word, "rd."))
- continue;
-
- if (value) {
- e = startswith(word, key);
- if (!e)
- continue;
-
- r = free_and_strdup(&ret, e);
- if (r < 0)
- return r;
-
- found = true;
- } else {
- if (streq(word, key))
- found = true;
- }
- }
-
- if (value) {
- *value = ret;
- ret = NULL;
- }
-
- return found;
-
-}
-
int container_get_leader(const char *machine, pid_t *pid) {
_cleanup_free_ char *s = NULL, *class = NULL;
const char *p;
@@ -5115,176 +763,6 @@ int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int
return reset_uid_gid();
}
-int getpeercred(int fd, struct ucred *ucred) {
- socklen_t n = sizeof(struct ucred);
- struct ucred u;
- int r;
-
- assert(fd >= 0);
- assert(ucred);
-
- r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &u, &n);
- if (r < 0)
- return -errno;
-
- if (n != sizeof(struct ucred))
- return -EIO;
-
- /* Check if the data is actually useful and not suppressed due
- * to namespacing issues */
- if (u.pid <= 0)
- return -ENODATA;
- if (u.uid == UID_INVALID)
- return -ENODATA;
- if (u.gid == GID_INVALID)
- return -ENODATA;
-
- *ucred = u;
- return 0;
-}
-
-int getpeersec(int fd, char **ret) {
- socklen_t n = 64;
- char *s;
- int r;
-
- assert(fd >= 0);
- assert(ret);
-
- s = new0(char, n);
- if (!s)
- return -ENOMEM;
-
- r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n);
- if (r < 0) {
- free(s);
-
- if (errno != ERANGE)
- return -errno;
-
- s = new0(char, n);
- if (!s)
- return -ENOMEM;
-
- r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n);
- if (r < 0) {
- free(s);
- return -errno;
- }
- }
-
- if (isempty(s)) {
- free(s);
- return -EOPNOTSUPP;
- }
-
- *ret = s;
- return 0;
-}
-
-/* This is much like like mkostemp() but is subject to umask(). */
-int mkostemp_safe(char *pattern, int flags) {
- _cleanup_umask_ mode_t u;
- int fd;
-
- assert(pattern);
-
- u = umask(077);
-
- fd = mkostemp(pattern, flags);
- if (fd < 0)
- return -errno;
-
- return fd;
-}
-
-int open_tmpfile(const char *path, int flags) {
- char *p;
- int fd;
-
- assert(path);
-
-#ifdef O_TMPFILE
- /* Try O_TMPFILE first, if it is supported */
- fd = open(path, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR);
- if (fd >= 0)
- return fd;
-#endif
-
- /* Fall back to unguessable name + unlinking */
- p = strjoina(path, "/systemd-tmp-XXXXXX");
-
- fd = mkostemp_safe(p, flags);
- if (fd < 0)
- return fd;
-
- unlink(p);
- return fd;
-}
-
-int fd_warn_permissions(const char *path, int fd) {
- struct stat st;
-
- if (fstat(fd, &st) < 0)
- return -errno;
-
- if (st.st_mode & 0111)
- log_warning("Configuration file %s is marked executable. Please remove executable permission bits. Proceeding anyway.", path);
-
- if (st.st_mode & 0002)
- log_warning("Configuration file %s is marked world-writable. Please remove world writability permission bits. Proceeding anyway.", path);
-
- if (getpid() == 1 && (st.st_mode & 0044) != 0044)
- log_warning("Configuration file %s is marked world-inaccessible. This has no effect as configuration data is accessible via APIs without restrictions. Proceeding anyway.", path);
-
- return 0;
-}
-
-unsigned long personality_from_string(const char *p) {
-
- /* Parse a personality specifier. We introduce our own
- * identifiers that indicate specific ABIs, rather than just
- * hints regarding the register size, since we want to keep
- * things open for multiple locally supported ABIs for the
- * same register size. We try to reuse the ABI identifiers
- * used by libseccomp. */
-
-#if defined(__x86_64__)
-
- if (streq(p, "x86"))
- return PER_LINUX32;
-
- if (streq(p, "x86-64"))
- return PER_LINUX;
-
-#elif defined(__i386__)
-
- if (streq(p, "x86"))
- return PER_LINUX;
-#endif
-
- return PERSONALITY_INVALID;
-}
-
-const char* personality_to_string(unsigned long p) {
-
-#if defined(__x86_64__)
-
- if (p == PER_LINUX32)
- return "x86";
-
- if (p == PER_LINUX)
- return "x86-64";
-
-#elif defined(__i386__)
-
- if (p == PER_LINUX)
- return "x86";
-#endif
-
- return NULL;
-}
-
uint64_t physical_memory(void) {
long mem;
@@ -5297,1575 +775,21 @@ uint64_t physical_memory(void) {
return (uint64_t) mem * (uint64_t) page_size();
}
-void hexdump(FILE *f, const void *p, size_t s) {
- const uint8_t *b = p;
- unsigned n = 0;
-
- assert(s == 0 || b);
-
- while (s > 0) {
- size_t i;
-
- fprintf(f, "%04x ", n);
-
- for (i = 0; i < 16; i++) {
-
- if (i >= s)
- fputs(" ", f);
- else
- fprintf(f, "%02x ", b[i]);
-
- if (i == 7)
- fputc(' ', f);
- }
-
- fputc(' ', f);
-
- for (i = 0; i < 16; i++) {
-
- if (i >= s)
- fputc(' ', f);
- else
- fputc(isprint(b[i]) ? (char) b[i] : '.', f);
- }
-
- fputc('\n', f);
-
- if (s < 16)
- break;
-
- n += 16;
- b += 16;
- s -= 16;
- }
-}
-
int update_reboot_param_file(const char *param) {
int r = 0;
if (param) {
-
r = write_string_file(REBOOT_PARAM_FILE, param, WRITE_STRING_FILE_CREATE);
if (r < 0)
- log_error("Failed to write reboot param to "
- REBOOT_PARAM_FILE": %s", strerror(-r));
- } else
- unlink(REBOOT_PARAM_FILE);
-
- return r;
-}
-
-int umount_recursive(const char *prefix, int flags) {
- bool again;
- int n = 0, r;
-
- /* Try to umount everything recursively below a
- * directory. Also, take care of stacked mounts, and keep
- * unmounting them until they are gone. */
-
- do {
- _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
-
- again = false;
- r = 0;
-
- proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
- if (!proc_self_mountinfo)
- return -errno;
-
- for (;;) {
- _cleanup_free_ char *path = NULL, *p = NULL;
- int k;
-
- k = fscanf(proc_self_mountinfo,
- "%*s " /* (1) mount id */
- "%*s " /* (2) parent id */
- "%*s " /* (3) major:minor */
- "%*s " /* (4) root */
- "%ms " /* (5) mount point */
- "%*s" /* (6) mount options */
- "%*[^-]" /* (7) optional fields */
- "- " /* (8) separator */
- "%*s " /* (9) file system type */
- "%*s" /* (10) mount source */
- "%*s" /* (11) mount options 2 */
- "%*[^\n]", /* some rubbish at the end */
- &path);
- if (k != 1) {
- if (k == EOF)
- break;
-
- continue;
- }
-
- r = cunescape(path, UNESCAPE_RELAX, &p);
- if (r < 0)
- return r;
-
- if (!path_startswith(p, prefix))
- continue;
-
- if (umount2(p, flags) < 0) {
- r = -errno;
- continue;
- }
-
- again = true;
- n++;
-
- break;
- }
-
- } while (again);
-
- return r ? r : n;
-}
-
-static int get_mount_flags(const char *path, unsigned long *flags) {
- struct statvfs buf;
-
- if (statvfs(path, &buf) < 0)
- return -errno;
- *flags = buf.f_flag;
- return 0;
-}
-
-int bind_remount_recursive(const char *prefix, bool ro) {
- _cleanup_set_free_free_ Set *done = NULL;
- _cleanup_free_ char *cleaned = NULL;
- int r;
-
- /* Recursively remount a directory (and all its submounts)
- * read-only or read-write. If the directory is already
- * mounted, we reuse the mount and simply mark it
- * MS_BIND|MS_RDONLY (or remove the MS_RDONLY for read-write
- * operation). If it isn't we first make it one. Afterwards we
- * apply MS_BIND|MS_RDONLY (or remove MS_RDONLY) to all
- * submounts we can access, too. When mounts are stacked on
- * the same mount point we only care for each individual
- * "top-level" mount on each point, as we cannot
- * influence/access the underlying mounts anyway. We do not
- * have any effect on future submounts that might get
- * propagated, they migt be writable. This includes future
- * submounts that have been triggered via autofs. */
-
- cleaned = strdup(prefix);
- if (!cleaned)
- return -ENOMEM;
-
- path_kill_slashes(cleaned);
-
- done = set_new(&string_hash_ops);
- if (!done)
- return -ENOMEM;
-
- for (;;) {
- _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
- _cleanup_set_free_free_ Set *todo = NULL;
- bool top_autofs = false;
- char *x;
- unsigned long orig_flags;
-
- todo = set_new(&string_hash_ops);
- if (!todo)
- return -ENOMEM;
-
- proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
- if (!proc_self_mountinfo)
- return -errno;
-
- for (;;) {
- _cleanup_free_ char *path = NULL, *p = NULL, *type = NULL;
- int k;
-
- k = fscanf(proc_self_mountinfo,
- "%*s " /* (1) mount id */
- "%*s " /* (2) parent id */
- "%*s " /* (3) major:minor */
- "%*s " /* (4) root */
- "%ms " /* (5) mount point */
- "%*s" /* (6) mount options (superblock) */
- "%*[^-]" /* (7) optional fields */
- "- " /* (8) separator */
- "%ms " /* (9) file system type */
- "%*s" /* (10) mount source */
- "%*s" /* (11) mount options (bind mount) */
- "%*[^\n]", /* some rubbish at the end */
- &path,
- &type);
- if (k != 2) {
- if (k == EOF)
- break;
-
- continue;
- }
-
- 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
- * them, as we don't make any guarantees for
- * future submounts anyway. If they are
- * already triggered, then we will find
- * another entry for this. */
- if (streq(type, "autofs")) {
- top_autofs = top_autofs || path_equal(cleaned, p);
- continue;
- }
-
- if (path_startswith(p, cleaned) &&
- !set_contains(done, p)) {
-
- r = set_consume(todo, p);
- p = NULL;
-
- if (r == -EEXIST)
- continue;
- if (r < 0)
- return r;
- }
- }
-
- /* If we have no submounts to process anymore and if
- * the root is either already done, or an autofs, we
- * are done */
- if (set_isempty(todo) &&
- (top_autofs || set_contains(done, cleaned)))
- return 0;
-
- if (!set_contains(done, cleaned) &&
- !set_contains(todo, cleaned)) {
- /* The prefix directory itself is not yet a
- * mount, make it one. */
- if (mount(cleaned, cleaned, NULL, MS_BIND|MS_REC, NULL) < 0)
- return -errno;
-
- orig_flags = 0;
- (void) get_mount_flags(cleaned, &orig_flags);
- orig_flags &= ~MS_RDONLY;
-
- if (mount(NULL, prefix, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0)
- return -errno;
-
- x = strdup(cleaned);
- if (!x)
- return -ENOMEM;
-
- r = set_consume(done, x);
- if (r < 0)
- return r;
- }
-
- while ((x = set_steal_first(todo))) {
-
- r = set_consume(done, x);
- if (r == -EEXIST || r == 0)
- continue;
- if (r < 0)
- return r;
-
- /* Try to reuse the original flag set, but
- * don't care for errors, in case of
- * obstructed mounts */
- orig_flags = 0;
- (void) get_mount_flags(x, &orig_flags);
- orig_flags &= ~MS_RDONLY;
-
- if (mount(NULL, x, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0) {
-
- /* Deal with mount points that are
- * obstructed by a later mount */
-
- if (errno != ENOENT)
- return -errno;
- }
-
- }
- }
-}
-
-int fflush_and_check(FILE *f) {
- assert(f);
-
- errno = 0;
- fflush(f);
-
- if (ferror(f))
- return errno ? -errno : -EIO;
-
- return 0;
-}
-
-int tempfn_xxxxxx(const char *p, const char *extra, char **ret) {
- const char *fn;
- char *t;
-
- assert(p);
- assert(ret);
-
- /*
- * Turns this:
- * /foo/bar/waldo
- *
- * Into this:
- * /foo/bar/.#<extra>waldoXXXXXX
- */
-
- fn = basename(p);
- if (!filename_is_valid(fn))
- return -EINVAL;
-
- if (extra == NULL)
- extra = "";
-
- t = new(char, strlen(p) + 2 + strlen(extra) + 6 + 1);
- if (!t)
- return -ENOMEM;
-
- strcpy(stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn), "XXXXXX");
-
- *ret = path_kill_slashes(t);
- return 0;
-}
-
-int tempfn_random(const char *p, const char *extra, char **ret) {
- const char *fn;
- char *t, *x;
- uint64_t u;
- unsigned i;
-
- assert(p);
- assert(ret);
-
- /*
- * Turns this:
- * /foo/bar/waldo
- *
- * Into this:
- * /foo/bar/.#<extra>waldobaa2a261115984a9
- */
-
- fn = basename(p);
- if (!filename_is_valid(fn))
- return -EINVAL;
-
- if (!extra)
- extra = "";
-
- t = new(char, strlen(p) + 2 + strlen(extra) + 16 + 1);
- if (!t)
- return -ENOMEM;
-
- x = stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn);
-
- u = random_u64();
- for (i = 0; i < 16; i++) {
- *(x++) = hexchar(u & 0xF);
- u >>= 4;
- }
-
- *x = 0;
-
- *ret = path_kill_slashes(t);
- return 0;
-}
-
-int tempfn_random_child(const char *p, const char *extra, char **ret) {
- char *t, *x;
- uint64_t u;
- unsigned i;
-
- assert(p);
- assert(ret);
-
- /* Turns this:
- * /foo/bar/waldo
- * Into this:
- * /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0
- */
-
- if (!extra)
- extra = "";
-
- t = new(char, strlen(p) + 3 + strlen(extra) + 16 + 1);
- if (!t)
- return -ENOMEM;
-
- x = stpcpy(stpcpy(stpcpy(t, p), "/.#"), extra);
-
- u = random_u64();
- for (i = 0; i < 16; i++) {
- *(x++) = hexchar(u & 0xF);
- u >>= 4;
- }
-
- *x = 0;
-
- *ret = path_kill_slashes(t);
- return 0;
-}
-
-int take_password_lock(const char *root) {
-
- struct flock flock = {
- .l_type = F_WRLCK,
- .l_whence = SEEK_SET,
- .l_start = 0,
- .l_len = 0,
- };
-
- const char *path;
- int fd, r;
-
- /* This is roughly the same as lckpwdf(), but not as awful. We
- * don't want to use alarm() and signals, hence we implement
- * our own trivial version of this.
- *
- * Note that shadow-utils also takes per-database locks in
- * addition to lckpwdf(). However, we don't given that they
- * are redundant as they they invoke lckpwdf() first and keep
- * it during everything they do. The per-database locks are
- * awfully racy, and thus we just won't do them. */
-
- if (root)
- path = strjoina(root, "/etc/.pwd.lock");
- else
- path = "/etc/.pwd.lock";
-
- fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600);
- if (fd < 0)
- return -errno;
-
- r = fcntl(fd, F_SETLKW, &flock);
- if (r < 0) {
- safe_close(fd);
- return -errno;
- }
-
- return fd;
-}
-
-int is_symlink(const char *path) {
- struct stat info;
-
- if (lstat(path, &info) < 0)
- return -errno;
-
- return !!S_ISLNK(info.st_mode);
-}
-
-int is_dir(const char* path, bool follow) {
- struct stat st;
- int r;
-
- if (follow)
- r = stat(path, &st);
- else
- r = lstat(path, &st);
- if (r < 0)
- return -errno;
-
- return !!S_ISDIR(st.st_mode);
-}
-
-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 extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags) {
- _cleanup_free_ char *s = NULL;
- size_t allocated = 0, sz = 0;
- int r;
-
- enum {
- START,
- VALUE,
- VALUE_ESCAPE,
- SINGLE_QUOTE,
- SINGLE_QUOTE_ESCAPE,
- DOUBLE_QUOTE,
- DOUBLE_QUOTE_ESCAPE,
- SEPARATOR,
- } state = START;
-
- assert(p);
- assert(ret);
-
- if (!separators)
- separators = WHITESPACE;
-
- /* Bail early if called after last value or with no input */
- if (!*p)
- goto finish_force_terminate;
-
- /* Parses the first word of a string, and returns it in
- * *ret. Removes all quotes in the process. When parsing fails
- * (because of an uneven number of quotes or similar), leaves
- * the pointer *p at the first invalid character. */
-
- for (;;) {
- char c = **p;
-
- switch (state) {
-
- case START:
- if (flags & EXTRACT_DONT_COALESCE_SEPARATORS)
- if (!GREEDY_REALLOC(s, allocated, sz+1))
- return -ENOMEM;
-
- if (c == 0)
- goto finish_force_terminate;
- else if (strchr(separators, c)) {
- if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) {
- (*p) ++;
- goto finish_force_next;
- }
- break;
- }
-
- /* We found a non-blank character, so we will always
- * want to return a string (even if it is empty),
- * allocate it here. */
- if (!GREEDY_REALLOC(s, allocated, sz+1))
- return -ENOMEM;
-
- state = VALUE;
- /* fallthrough */
-
- case VALUE:
- if (c == 0)
- goto finish_force_terminate;
- else if (c == '\'' && (flags & EXTRACT_QUOTES))
- state = SINGLE_QUOTE;
- else if (c == '\\')
- state = VALUE_ESCAPE;
- else if (c == '\"' && (flags & EXTRACT_QUOTES))
- state = DOUBLE_QUOTE;
- else if (strchr(separators, c)) {
- if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) {
- (*p) ++;
- goto finish_force_next;
- }
- state = SEPARATOR;
- } else {
- if (!GREEDY_REALLOC(s, allocated, sz+2))
- return -ENOMEM;
-
- s[sz++] = c;
- }
-
- break;
-
- case SINGLE_QUOTE:
- if (c == 0) {
- if (flags & EXTRACT_RELAX)
- goto finish_force_terminate;
- return -EINVAL;
- } else if (c == '\'')
- state = VALUE;
- else if (c == '\\')
- state = SINGLE_QUOTE_ESCAPE;
- else {
- if (!GREEDY_REALLOC(s, allocated, sz+2))
- return -ENOMEM;
-
- s[sz++] = c;
- }
-
- break;
-
- case DOUBLE_QUOTE:
- if (c == 0)
- return -EINVAL;
- else if (c == '\"')
- state = VALUE;
- else if (c == '\\')
- state = DOUBLE_QUOTE_ESCAPE;
- else {
- if (!GREEDY_REALLOC(s, allocated, sz+2))
- return -ENOMEM;
-
- s[sz++] = c;
- }
-
- break;
-
- case SINGLE_QUOTE_ESCAPE:
- case DOUBLE_QUOTE_ESCAPE:
- case VALUE_ESCAPE:
- if (!GREEDY_REALLOC(s, allocated, sz+7))
- return -ENOMEM;
-
- if (c == 0) {
- if ((flags & EXTRACT_CUNESCAPE_RELAX) &&
- (state == VALUE_ESCAPE || flags & EXTRACT_RELAX)) {
- /* If we find an unquoted trailing backslash and we're in
- * EXTRACT_CUNESCAPE_RELAX mode, keep it verbatim in the
- * output.
- *
- * Unbalanced quotes will only be allowed in EXTRACT_RELAX
- * mode, EXTRACT_CUNESCAPE_RELAX mode does not allow them.
- */
- s[sz++] = '\\';
- goto finish_force_terminate;
- }
- if (flags & EXTRACT_RELAX)
- goto finish_force_terminate;
- return -EINVAL;
- }
-
- if (flags & EXTRACT_CUNESCAPE) {
- uint32_t u;
-
- r = cunescape_one(*p, (size_t) -1, &c, &u);
- if (r < 0) {
- if (flags & EXTRACT_CUNESCAPE_RELAX) {
- s[sz++] = '\\';
- s[sz++] = c;
- goto end_escape;
- }
- 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;
-
-end_escape:
- state = (state == SINGLE_QUOTE_ESCAPE) ? SINGLE_QUOTE :
- (state == DOUBLE_QUOTE_ESCAPE) ? DOUBLE_QUOTE :
- VALUE;
- break;
-
- case SEPARATOR:
- if (c == 0)
- goto finish_force_terminate;
- if (!strchr(separators, c))
- goto finish;
- break;
- }
-
- (*p) ++;
- }
-
-finish_force_terminate:
- *p = NULL;
-finish:
- if (!s) {
- *p = NULL;
- *ret = NULL;
- return 0;
- }
-
-finish_force_next:
- s[sz] = 0;
- *ret = s;
- s = NULL;
-
- return 1;
-}
-
-int extract_first_word_and_warn(
- const char **p,
- char **ret,
- const char *separators,
- ExtractFlags flags,
- const char *unit,
- const char *filename,
- unsigned line,
- const char *rvalue) {
- /* Try to unquote it, if it fails, warn about it and try again but this
- * time using EXTRACT_CUNESCAPE_RELAX to keep the backslashes verbatim
- * in invalid escape sequences. */
- const char *save;
- int r;
-
- save = *p;
- r = extract_first_word(p, ret, separators, flags);
- if (r < 0 && !(flags&EXTRACT_CUNESCAPE_RELAX)) {
- /* Retry it with EXTRACT_CUNESCAPE_RELAX. */
- *p = save;
- r = extract_first_word(p, ret, separators, flags|EXTRACT_CUNESCAPE_RELAX);
- if (r < 0)
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Unbalanced quoting in command line, ignoring: \"%s\"", rvalue);
- else
- log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
- "Invalid escape sequences in command line: \"%s\"", rvalue);
- }
- return r;
-}
-
-int extract_many_words(const char **p, const char *separators, ExtractFlags flags, ...) {
- va_list ap;
- char **l;
- int n = 0, i, c, r;
-
- /* Parses a number of words from a string, stripping any
- * quotes if necessary. */
-
- assert(p);
-
- /* Count how many words are expected */
- va_start(ap, flags);
- for (;;) {
- if (!va_arg(ap, char **))
- break;
- n++;
- }
- va_end(ap);
-
- if (n <= 0)
- return 0;
-
- /* Read all words into a temporary array */
- l = newa0(char*, n);
- for (c = 0; c < n; c++) {
-
- r = extract_first_word(p, &l[c], separators, flags);
- if (r < 0) {
- int j;
-
- for (j = 0; j < c; j++)
- free(l[j]);
-
- return r;
- }
-
- if (r == 0)
- break;
- }
-
- /* If we managed to parse all words, return them in the passed
- * in parameters */
- va_start(ap, flags);
- for (i = 0; i < n; i++) {
- char **v;
-
- v = va_arg(ap, char **);
- assert(v);
-
- *v = l[i];
- }
- va_end(ap);
-
- return c;
-}
-
-int free_and_strdup(char **p, const char *s) {
- char *t;
-
- assert(p);
-
- /* 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)
- return -ENOMEM;
- } else
- t = NULL;
-
- free(*p);
- *p = t;
-
- return 1;
-}
-
-int ptsname_malloc(int fd, char **ret) {
- size_t l = 100;
-
- assert(fd >= 0);
- assert(ret);
-
- for (;;) {
- char *c;
-
- c = new(char, l);
- if (!c)
- return -ENOMEM;
-
- if (ptsname_r(fd, c, l) == 0) {
- *ret = c;
- return 0;
- }
- if (errno != ERANGE) {
- free(c);
- return -errno;
- }
-
- free(c);
- l *= 2;
- }
-}
-
-int openpt_in_namespace(pid_t pid, int flags) {
- _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
- _cleanup_close_pair_ int pair[2] = { -1, -1 };
- union {
- struct cmsghdr cmsghdr;
- uint8_t buf[CMSG_SPACE(sizeof(int))];
- } control = {};
- struct msghdr mh = {
- .msg_control = &control,
- .msg_controllen = sizeof(control),
- };
- struct cmsghdr *cmsg;
- siginfo_t si;
- pid_t child;
- int r;
-
- assert(pid > 0);
-
- r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd);
- if (r < 0)
- return r;
-
- if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
- return -errno;
-
- child = fork();
- if (child < 0)
- return -errno;
-
- if (child == 0) {
- int master;
-
- pair[0] = safe_close(pair[0]);
-
- r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd);
- if (r < 0)
- _exit(EXIT_FAILURE);
-
- master = posix_openpt(flags);
- if (master < 0)
- _exit(EXIT_FAILURE);
-
- if (unlockpt(master) < 0)
- _exit(EXIT_FAILURE);
-
- cmsg = CMSG_FIRSTHDR(&mh);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(sizeof(int));
- memcpy(CMSG_DATA(cmsg), &master, sizeof(int));
-
- mh.msg_controllen = cmsg->cmsg_len;
-
- if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
- _exit(EXIT_FAILURE);
-
- _exit(EXIT_SUCCESS);
- }
-
- pair[1] = safe_close(pair[1]);
-
- r = wait_for_terminate(child, &si);
- if (r < 0)
- return r;
- if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
- return -EIO;
-
- if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
- return -errno;
-
- CMSG_FOREACH(cmsg, &mh)
- if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
- int *fds;
- unsigned n_fds;
-
- fds = (int*) CMSG_DATA(cmsg);
- n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
-
- if (n_fds != 1) {
- close_many(fds, n_fds);
- return -EIO;
- }
-
- return fds[0];
- }
-
- return -EIO;
-}
-
-ssize_t fgetxattrat_fake(int dirfd, const char *filename, const char *attribute, void *value, size_t size, int flags) {
- _cleanup_close_ int fd = -1;
- ssize_t l;
-
- /* The kernel doesn't have a fgetxattrat() command, hence let's emulate one */
-
- fd = openat(dirfd, filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOATIME|(flags & AT_SYMLINK_NOFOLLOW ? O_NOFOLLOW : 0));
- if (fd < 0)
- return -errno;
-
- l = fgetxattr(fd, attribute, value, size);
- if (l < 0)
- return -errno;
-
- return l;
-}
-
-static int parse_crtime(le64_t le, usec_t *usec) {
- uint64_t u;
-
- assert(usec);
-
- u = le64toh(le);
- if (u == 0 || u == (uint64_t) -1)
- return -EIO;
-
- *usec = (usec_t) u;
- return 0;
-}
-
-int fd_getcrtime(int fd, usec_t *usec) {
- le64_t le;
- ssize_t n;
-
- assert(fd >= 0);
- assert(usec);
-
- /* Until Linux gets a real concept of birthtime/creation time,
- * let's fake one with xattrs */
-
- n = fgetxattr(fd, "user.crtime_usec", &le, sizeof(le));
- if (n < 0)
- return -errno;
- if (n != sizeof(le))
- return -EIO;
-
- return parse_crtime(le, usec);
-}
-
-int fd_getcrtime_at(int dirfd, const char *name, usec_t *usec, int flags) {
- le64_t le;
- ssize_t n;
-
- n = fgetxattrat_fake(dirfd, name, "user.crtime_usec", &le, sizeof(le), flags);
- if (n < 0)
- return -errno;
- if (n != sizeof(le))
- return -EIO;
-
- return parse_crtime(le, usec);
-}
-
-int path_getcrtime(const char *p, usec_t *usec) {
- le64_t le;
- ssize_t n;
-
- assert(p);
- assert(usec);
-
- n = getxattr(p, "user.crtime_usec", &le, sizeof(le));
- if (n < 0)
- return -errno;
- if (n != sizeof(le))
- return -EIO;
-
- return parse_crtime(le, usec);
-}
-
-int fd_setcrtime(int fd, usec_t usec) {
- le64_t le;
-
- assert(fd >= 0);
-
- if (usec <= 0)
- usec = now(CLOCK_REALTIME);
-
- le = htole64((uint64_t) usec);
- if (fsetxattr(fd, "user.crtime_usec", &le, sizeof(le), 0) < 0)
- return -errno;
-
- return 0;
-}
-
-int same_fd(int a, int b) {
- struct stat sta, stb;
- pid_t pid;
- int r, fa, fb;
-
- assert(a >= 0);
- assert(b >= 0);
-
- /* Compares two file descriptors. Note that semantics are
- * quite different depending on whether we have kcmp() or we
- * don't. If we have kcmp() this will only return true for
- * dup()ed file descriptors, but not otherwise. If we don't
- * have kcmp() this will also return true for two fds of the same
- * file, created by separate open() calls. Since we use this
- * call mostly for filtering out duplicates in the fd store
- * this difference hopefully doesn't matter too much. */
-
- if (a == b)
- return true;
-
- /* Try to use kcmp() if we have it. */
- pid = getpid();
- r = kcmp(pid, pid, KCMP_FILE, a, b);
- if (r == 0)
- return true;
- if (r > 0)
- return false;
- if (errno != ENOSYS)
- return -errno;
-
- /* We don't have kcmp(), use fstat() instead. */
- if (fstat(a, &sta) < 0)
- return -errno;
-
- if (fstat(b, &stb) < 0)
- return -errno;
-
- if ((sta.st_mode & S_IFMT) != (stb.st_mode & S_IFMT))
- return false;
-
- /* We consider all device fds different, since two device fds
- * might refer to quite different device contexts even though
- * they share the same inode and backing dev_t. */
-
- if (S_ISCHR(sta.st_mode) || S_ISBLK(sta.st_mode))
- return false;
-
- if (sta.st_dev != stb.st_dev || sta.st_ino != stb.st_ino)
- return false;
-
- /* The fds refer to the same inode on disk, let's also check
- * if they have the same fd flags. This is useful to
- * distinguish the read and write side of a pipe created with
- * pipe(). */
- fa = fcntl(a, F_GETFL);
- if (fa < 0)
- return -errno;
-
- fb = fcntl(b, F_GETFL);
- if (fb < 0)
- return -errno;
-
- return fa == fb;
-}
-
-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;
-
- 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 1;
-}
-
-int chattr_path(const char *p, unsigned value, unsigned mask) {
- _cleanup_close_ int fd = -1;
-
- assert(p);
-
- if (mask == 0)
- return 0;
-
- fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
- if (fd < 0)
- return -errno;
-
- 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;
-
- return 0;
-}
-
-int read_attr_path(const char *p, unsigned *ret) {
- _cleanup_close_ int fd = -1;
-
- assert(p);
- assert(ret);
-
- fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
- if (fd < 0)
- return -errno;
-
- return read_attr_fd(fd, ret);
-}
-
-static size_t nul_length(const uint8_t *p, size_t sz) {
- size_t n = 0;
-
- while (sz > 0) {
- if (*p != 0)
- break;
-
- n++;
- p++;
- sz--;
- }
-
- return n;
-}
-
-ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length) {
- const uint8_t *q, *w, *e;
- ssize_t l;
-
- q = w = p;
- e = q + sz;
- while (q < e) {
- size_t n;
-
- n = nul_length(q, e - q);
-
- /* If there are more than the specified run length of
- * NUL bytes, or if this is the beginning or the end
- * of the buffer, then seek instead of write */
- if ((n > run_length) ||
- (n > 0 && q == p) ||
- (n > 0 && q + n >= e)) {
- if (q > w) {
- l = write(fd, w, q - w);
- if (l < 0)
- return -errno;
- if (l != q -w)
- return -EIO;
- }
-
- if (lseek(fd, n, SEEK_CUR) == (off_t) -1)
- return -errno;
-
- q += n;
- w = q;
- } else if (n > 0)
- q += n;
- else
- q ++;
- }
-
- if (q > w) {
- l = write(fd, w, q - w);
- if (l < 0)
- return -errno;
- if (l != q - w)
- return -EIO;
- }
-
- return q - (const uint8_t*) p;
-}
-
-void sigkill_wait(pid_t *pid) {
- if (!pid)
- return;
- if (*pid <= 1)
- return;
-
- if (kill(*pid, SIGKILL) > 0)
- (void) wait_for_terminate(*pid, NULL);
-}
-
-int syslog_parse_priority(const char **p, int *priority, bool with_facility) {
- int a = 0, b = 0, c = 0;
- int k;
-
- assert(p);
- assert(*p);
- assert(priority);
-
- if ((*p)[0] != '<')
- return 0;
-
- if (!strchr(*p, '>'))
- return 0;
-
- if ((*p)[2] == '>') {
- c = undecchar((*p)[1]);
- k = 3;
- } else if ((*p)[3] == '>') {
- b = undecchar((*p)[1]);
- c = undecchar((*p)[2]);
- k = 4;
- } else if ((*p)[4] == '>') {
- a = undecchar((*p)[1]);
- b = undecchar((*p)[2]);
- c = undecchar((*p)[3]);
- k = 5;
+ return log_error_errno(r, "Failed to write reboot param to "REBOOT_PARAM_FILE": %m");
} else
- return 0;
-
- if (a < 0 || b < 0 || c < 0 ||
- (!with_facility && (a || b || c > 7)))
- return 0;
-
- if (with_facility)
- *priority = a*100 + b*10 + c;
- else
- *priority = (*priority & LOG_FACMASK) | c;
-
- *p += k;
- return 1;
-}
-
-ssize_t string_table_lookup(const char * const *table, size_t len, const char *key) {
- size_t i;
-
- if (!key)
- return -1;
-
- for (i = 0; i < len; ++i)
- if (streq_ptr(table[i], key))
- return (ssize_t) i;
-
- return -1;
-}
-
-void cmsg_close_all(struct msghdr *mh) {
- struct cmsghdr *cmsg;
-
- assert(mh);
-
- CMSG_FOREACH(cmsg, mh)
- 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;
-
- /* renameat2() exists since Linux 3.15, btrfs added support for it later.
- * If it is not implemented, fallback to another method. */
- if (!IN_SET(errno, EINVAL, ENOSYS))
- 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;
-}
-
-static char *strcpy_backslash_escaped(char *t, const char *s, const char *bad) {
- assert(bad);
-
- for (; *s; s++) {
- if (*s == '\\' || strchr(bad, *s))
- *(t++) = '\\';
-
- *(t++) = *s;
- }
-
- return t;
-}
-
-char *shell_escape(const char *s, const char *bad) {
- char *r, *t;
-
- r = new(char, strlen(s)*2+1);
- if (!r)
- return NULL;
-
- t = strcpy_backslash_escaped(r, s, bad);
- *t = 0;
-
- return r;
-}
-
-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);
-
- t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE);
-
- *(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;
+ (void) unlink(REBOOT_PARAM_FILE);
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;
-}
-
-int getxattr_malloc(const char *path, const char *name, char **value, bool allow_symlink) {
- char *v;
- size_t l;
- ssize_t n;
-
- assert(path);
- assert(name);
- assert(value);
-
- for (l = 100; ; l = (size_t) n + 1) {
- v = new0(char, l);
- if (!v)
- return -ENOMEM;
-
- if (allow_symlink)
- n = lgetxattr(path, name, v, l);
- else
- n = getxattr(path, name, v, l);
-
- if (n >= 0 && (size_t) n < l) {
- *value = v;
- return n;
- }
-
- free(v);
-
- if (n < 0 && errno != ERANGE)
- return -errno;
-
- if (allow_symlink)
- n = lgetxattr(path, name, NULL, 0);
- else
- n = getxattr(path, name, NULL, 0);
- if (n < 0)
- return -errno;
- }
-}
-
-int fgetxattr_malloc(int fd, const char *name, char **value) {
- char *v;
- size_t l;
- ssize_t n;
-
- assert(fd >= 0);
- assert(name);
- assert(value);
-
- for (l = 100; ; l = (size_t) n + 1) {
- v = new0(char, l);
- if (!v)
- return -ENOMEM;
-
- n = fgetxattr(fd, name, v, l);
-
- if (n >= 0 && (size_t) n < l) {
- *value = v;
- return n;
- }
-
- free(v);
-
- if (n < 0 && errno != ERANGE)
- return -errno;
-
- n = fgetxattr(fd, name, NULL, 0);
- if (n < 0)
- return -errno;
- }
-}
-
-int send_one_fd(int transport_fd, int fd) {
- union {
- struct cmsghdr cmsghdr;
- uint8_t buf[CMSG_SPACE(sizeof(int))];
- } control = {};
- struct msghdr mh = {
- .msg_control = &control,
- .msg_controllen = sizeof(control),
- };
- struct cmsghdr *cmsg;
- ssize_t k;
-
- assert(transport_fd >= 0);
- assert(fd >= 0);
-
- cmsg = CMSG_FIRSTHDR(&mh);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(sizeof(int));
- memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
-
- mh.msg_controllen = CMSG_SPACE(sizeof(int));
- k = sendmsg(transport_fd, &mh, MSG_NOSIGNAL);
- if (k < 0)
- return -errno;
-
+int version(void) {
+ puts(PACKAGE_STRING "\n"
+ SYSTEMD_FEATURES);
return 0;
}
-
-int receive_one_fd(int transport_fd) {
- union {
- struct cmsghdr cmsghdr;
- uint8_t buf[CMSG_SPACE(sizeof(int))];
- } control = {};
- struct msghdr mh = {
- .msg_control = &control,
- .msg_controllen = sizeof(control),
- };
- struct cmsghdr *cmsg;
- ssize_t k;
-
- assert(transport_fd >= 0);
-
- /*
- * Receive a single FD via @transport_fd. We don't care for the
- * transport-type, but the caller must assure that no other CMSG types
- * than SCM_RIGHTS is enabled. We also retrieve a single FD at most, so
- * for packet-based transports, the caller must ensure to send only a
- * single FD per packet.
- * This is best used in combination with send_one_fd().
- */
-
- k = recvmsg(transport_fd, &mh, MSG_NOSIGNAL | MSG_CMSG_CLOEXEC);
- if (k < 0)
- return -errno;
-
- cmsg = CMSG_FIRSTHDR(&mh);
- if (!cmsg || CMSG_NXTHDR(&mh, cmsg) ||
- cmsg->cmsg_level != SOL_SOCKET ||
- cmsg->cmsg_type != SCM_RIGHTS ||
- cmsg->cmsg_len != CMSG_LEN(sizeof(int)) ||
- *(const int *)CMSG_DATA(cmsg) < 0) {
- cmsg_close_all(&mh);
- return -EIO;
- }
-
- return *(const int *)CMSG_DATA(cmsg);
-}
diff --git a/src/basic/util.h b/src/basic/util.h
index d53e15e6e6..76a06822b7 100644
--- a/src/basic/util.h
+++ b/src/basic/util.h
@@ -22,18 +22,18 @@
***/
#include <alloca.h>
-#include <dirent.h>
+#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
#include <locale.h>
-#include <mntent.h>
-#include <sched.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/inotify.h>
#include <sys/socket.h>
#include <sys/stat.h>
@@ -47,49 +47,9 @@
#include "missing.h"
#include "time-util.h"
-/* What is interpreted as whitespace? */
-#define WHITESPACE " \t\n\r"
-#define NEWLINE "\n\r"
-#define QUOTES "\"\'"
-#define COMMENTS "#;"
-#define GLOB_CHARS "*?["
-
-/* What characters are special in the shell? */
-/* must be escaped outside and inside double-quotes */
-#define SHELL_NEED_ESCAPE "\"\\`$"
-/* can be escaped or double-quoted */
-#define SHELL_NEED_QUOTES SHELL_NEED_ESCAPE GLOB_CHARS "'()<>|&;"
-
-#define FORMAT_BYTES_MAX 8
-
size_t page_size(void) _pure_;
#define PAGE_ALIGN(l) ALIGN_TO((l), page_size())
-#define streq(a,b) (strcmp((a),(b)) == 0)
-#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0)
-#define strcaseeq(a,b) (strcasecmp((a),(b)) == 0)
-#define strncaseeq(a, b, n) (strncasecmp((a), (b), (n)) == 0)
-
-bool streq_ptr(const char *a, const char *b) _pure_;
-int strcmp_ptr(const char *a, const char *b) _pure_;
-
-#define new(t, n) ((t*) malloc_multiply(sizeof(t), (n)))
-
-#define new0(t, n) ((t*) calloc((n), sizeof(t)))
-
-#define newa(t, n) ((t*) alloca(sizeof(t)*(n)))
-
-#define newa0(t, n) ((t*) alloca0(sizeof(t)*(n)))
-
-#define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n)))
-
-#define malloc0(n) (calloc(1, (n)))
-
-static inline void *mfree(void *memory) {
- free(memory);
- return NULL;
-}
-
static inline const char* yes_no(bool b) {
return b ? "yes" : "no";
}
@@ -102,350 +62,13 @@ static inline const char* one_zero(bool b) {
return b ? "1" : "0";
}
-static inline const char* strempty(const char *s) {
- return s ? s : "";
-}
-
-static inline const char* strnull(const char *s) {
- return s ? s : "(null)";
-}
-
-static inline const char *strna(const char *s) {
- return s ? s : "n/a";
-}
-
-static inline bool isempty(const char *p) {
- return !p || !p[0];
-}
-
-static inline char *startswith(const char *s, const char *prefix) {
- size_t l;
-
- l = strlen(prefix);
- if (strncmp(s, prefix, l) == 0)
- return (char*) s + l;
-
- return NULL;
-}
-
-static inline char *startswith_no_case(const char *s, const char *prefix) {
- size_t l;
-
- l = strlen(prefix);
- if (strncasecmp(s, prefix, l) == 0)
- return (char*) s + l;
-
- return NULL;
-}
-
-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_;
-
-int close_nointr(int fd);
-int safe_close(int fd);
-void safe_close_pair(int p[]);
-
-void close_many(const int fds[], unsigned n_fd);
-
-int fclose_nointr(FILE *f);
-FILE* safe_fclose(FILE *f);
-DIR* safe_closedir(DIR *f);
-
-int parse_size(const char *t, uint64_t base, uint64_t *size);
-
-int parse_boolean(const char *v) _pure_;
-int parse_pid(const char *s, pid_t* ret_pid);
-int parse_uid(const char *s, uid_t* ret_uid);
-#define parse_gid(s, ret_gid) parse_uid(s, ret_gid)
-
-bool uid_is_valid(uid_t uid);
-
-static inline bool gid_is_valid(gid_t gid) {
- return uid_is_valid((uid_t) gid);
-}
-
-int safe_atou(const char *s, unsigned *ret_u);
-int safe_atoi(const char *s, int *ret_i);
-
-int safe_atollu(const char *s, unsigned long long *ret_u);
-int safe_atolli(const char *s, long long int *ret_i);
-
-int safe_atod(const char *s, double *ret_d);
-
-int safe_atou8(const char *s, uint8_t *ret);
-
-#if LONG_MAX == INT_MAX
-static inline int safe_atolu(const char *s, unsigned long *ret_u) {
- assert_cc(sizeof(unsigned long) == sizeof(unsigned));
- return safe_atou(s, (unsigned*) ret_u);
-}
-static inline int safe_atoli(const char *s, long int *ret_u) {
- assert_cc(sizeof(long int) == sizeof(int));
- return safe_atoi(s, (int*) ret_u);
-}
-#else
-static inline int safe_atolu(const char *s, unsigned long *ret_u) {
- assert_cc(sizeof(unsigned long) == sizeof(unsigned long long));
- return safe_atollu(s, (unsigned long long*) ret_u);
-}
-static inline int safe_atoli(const char *s, long int *ret_u) {
- assert_cc(sizeof(long int) == sizeof(long long int));
- return safe_atolli(s, (long long int*) ret_u);
-}
-#endif
-
-static inline int safe_atou32(const char *s, uint32_t *ret_u) {
- assert_cc(sizeof(uint32_t) == sizeof(unsigned));
- return safe_atou(s, (unsigned*) ret_u);
-}
-
-static inline int safe_atoi32(const char *s, int32_t *ret_i) {
- assert_cc(sizeof(int32_t) == sizeof(int));
- return safe_atoi(s, (int*) ret_i);
-}
-
-static inline int safe_atou64(const char *s, uint64_t *ret_u) {
- assert_cc(sizeof(uint64_t) == sizeof(unsigned long long));
- return safe_atollu(s, (unsigned long long*) ret_u);
-}
-
-static inline int safe_atoi64(const char *s, int64_t *ret_i) {
- assert_cc(sizeof(int64_t) == sizeof(long long int));
- return safe_atolli(s, (long long int*) ret_i);
-}
-
-int safe_atou16(const char *s, uint16_t *ret);
-int safe_atoi16(const char *s, int16_t *ret);
-
-const char* split(const char **state, size_t *l, const char *separator, bool quoted);
-
-#define FOREACH_WORD(word, length, s, state) \
- _FOREACH_WORD(word, length, s, WHITESPACE, false, state)
-
-#define FOREACH_WORD_SEPARATOR(word, length, s, separator, state) \
- _FOREACH_WORD(word, length, s, separator, false, state)
-
-#define FOREACH_WORD_QUOTED(word, length, s, state) \
- _FOREACH_WORD(word, length, s, WHITESPACE, true, state)
-
-#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)))
-
-char *strappend(const char *s, const char *suffix);
-char *strnappend(const char *s, const char *suffix, size_t length);
-
-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);
-
-char *strstrip(char *s);
-char *delete_chars(char *s, const char *bad);
-char *truncate_nl(char *s);
-
-char *file_in_same_dir(const char *path, const char *filename);
-
-int rmdir_parents(const char *path, const char *stop);
-
-char hexchar(int x) _const_;
-int unhexchar(char c) _const_;
-char octchar(int x) _const_;
-int unoctchar(char c) _const_;
-char decchar(int x) _const_;
-int undecchar(char c) _const_;
-char base32hexchar(int x) _const_;
-int unbase32hexchar(char c) _const_;
-char base64char(int x) _const_;
-int unbase64char(char c) _const_;
-
-char *cescape(const char *s);
-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);
-
-char *ascii_strlower(char *path);
-
-bool dirent_is_file(const struct dirent *de) _pure_;
-bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) _pure_;
-
-bool hidden_file(const char *filename) _pure_;
-
-bool chars_intersect(const char *a, const char *b) _pure_;
-
-/* 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) { \
- if (i < 0 || i >= (type) ELEMENTSOF(name##_table)) \
- return NULL; \
- return name##_table[i]; \
- }
-
-ssize_t string_table_lookup(const char * const *table, size_t len, const char *key);
-
-#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) \
- scope type name##_from_string(const char *s) { \
- return (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \
- }
-
-#define _DEFINE_STRING_TABLE_LOOKUP(name,type,scope) \
- _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \
- _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) \
- struct __useless_struct_to_allow_trailing_semicolon__
-
-#define DEFINE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,)
-#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,static)
-#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,static)
-#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,static)
-
-/* For string conversions where numbers are also acceptable */
-#define DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(name,type,max) \
- int name##_to_string_alloc(type i, char **str) { \
- char *s; \
- if (i < 0 || i > max) \
- return -ERANGE; \
- if (i < (type) ELEMENTSOF(name##_table)) { \
- s = strdup(name##_table[i]); \
- if (!s) \
- return -ENOMEM; \
- } else { \
- if (asprintf(&s, "%i", i) < 0) \
- return -ENOMEM; \
- } \
- *str = s; \
- return 0; \
- } \
- type name##_from_string(const char *s) { \
- type i; \
- unsigned u = 0; \
- if (!s) \
- return (type) -1; \
- for (i = 0; i < (type) ELEMENTSOF(name##_table); i++) \
- if (streq_ptr(name##_table[i], s)) \
- return i; \
- if (safe_atou(s, &u) >= 0 && u <= max) \
- return (type) u; \
- return (type) -1; \
- } \
- struct __useless_struct_to_allow_trailing_semicolon__
-
-int fd_nonblock(int fd, bool nonblock);
-int fd_cloexec(int fd, bool cloexec);
-
-int close_all_fds(const int except[], unsigned n_except);
-
-bool fstype_is_network(const char *fstype);
-
-int flush_fd(int fd);
-
-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);
-
-int dir_is_empty(const char *path);
-char* dirname_malloc(const char *path);
-
-char* lookup_uid(uid_t uid);
-char* getlogname_malloc(void);
-char* getusername_malloc(void);
-
-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);
-
-bool is_temporary_fs(const struct statfs *s) _pure_;
-int fd_is_temporary_fs(int fd);
-
-int pipe_eof(int fd);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(cpu_set_t*, CPU_FREE);
-#define _cleanup_cpu_free_ _cleanup_(CPU_FREEp)
-
-cpu_set_t* cpu_set_malloc(unsigned *ncpus);
-
-#define xsprintf(buf, fmt, ...) \
- assert_message_se((size_t) snprintf(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__) < ELEMENTSOF(buf), \
- "xsprintf: " #buf "[] must be big enough")
-
-int files_same(const char *filea, const char *fileb);
-
-int running_in_chroot(void);
-
-char *ellipsize(const char *s, size_t length, unsigned percent);
- /* bytes columns */
-char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent);
-
-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);
-
-noreturn void freeze(void);
-
-bool null_or_empty(struct stat *st) _pure_;
-int null_or_empty_path(const char *fn);
-int null_or_empty_fd(int fd);
-
-DIR *xopendirat(int dirfd, const char *name, int flags);
-
-char *fstab_node_to_udev_node(const char *p);
-
void execute_directories(const char* const* directories, usec_t timeout, char *argv[]);
-bool nulstr_contains(const char*nulstr, const char *needle);
-
bool plymouth_running(void);
-char* strshorten(char *s, size_t l);
-
-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);
-int mkfifo_atomic(const char *path, mode_t mode);
-
-int fchmod_umask(int fd, mode_t mode);
-
bool display_is_local(const char *display) _pure_;
int socket_from_display(const char *display, char **path);
-int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell);
-int get_group_creds(const char **groupname, gid_t *gid);
-
-int in_gid(gid_t gid);
-int in_group(const char *name);
-
-char* uid_to_name(uid_t uid);
-char* gid_to_name(gid_t gid);
-
-int glob_exists(const char *path);
-int glob_extend(char ***strv, const char *path);
-
-int dirent_ensure_type(DIR *d, struct dirent *de);
-
-int get_files_in_directory(const char *path, char ***list);
-
-char *strjoin(const char *x, ...) _sentinel_;
-
-bool is_main_thread(void);
-
-static inline bool _pure_ in_charset(const char *s, const char* charset) {
- assert(s);
- assert(charset);
- return s[strspn(s, charset)] == '\0';
-}
-
int block_get_whole_disk(dev_t d, dev_t *ret);
#define NULSTR_FOREACH(i, l) \
@@ -454,27 +77,6 @@ int block_get_whole_disk(dev_t d, dev_t *ret);
#define NULSTR_FOREACH_PAIR(i, j, l) \
for ((i) = (l), (j) = strchr((i), 0)+1; (i) && *(i); (i) = strchr((j), 0)+1, (j) = *(i) ? strchr((i), 0)+1 : (i))
-int ioprio_class_to_string_alloc(int i, char **s);
-int ioprio_class_from_string(const char *s);
-
-const char *sigchld_code_to_string(int i) _const_;
-int sigchld_code_from_string(const char *s) _pure_;
-
-int log_facility_unshifted_to_string_alloc(int i, char **s);
-int log_facility_unshifted_from_string(const char *s);
-
-int log_level_to_string_alloc(int i, char **s);
-int log_level_from_string(const char *s);
-
-int sched_policy_to_string_alloc(int i, char **s);
-int sched_policy_from_string(const char *s);
-
-const char *rlimit_to_string(int i) _const_;
-int rlimit_from_string(const char *s) _pure_;
-
-int ip_tos_to_string_alloc(int i, char **s);
-int ip_tos_from_string(const char *s);
-
extern int saved_argc;
extern char **saved_argv;
@@ -482,182 +84,36 @@ bool kexec_loaded(void);
int prot_from_flags(int flags) _const_;
-char *format_bytes(char *buf, size_t l, uint64_t t);
-
-int fd_wait_for_event(int fd, int event, usec_t timeout);
-
-void* memdup(const void *p, size_t l) _alloc_(2);
-
-int fd_inc_sndbuf(int fd, size_t n);
-int fd_inc_rcvbuf(int fd, size_t n);
-
int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...);
-int setrlimit_closest(int resource, const struct rlimit *rlim);
-
-bool http_url_is_valid(const char *url) _pure_;
-bool documentation_url_is_valid(const char *url) _pure_;
-
-bool http_etag_is_valid(const char *etag);
-
bool in_initrd(void);
-int get_home_dir(char **ret);
-int get_shell(char **_ret);
-
-static inline void freep(void *p) {
- free(*(void**) p);
-}
-
-static inline void closep(int *fd) {
- safe_close(*fd);
-}
-
-static inline void umaskp(mode_t *u) {
- umask(*u);
-}
-
-static inline void close_pairp(int (*p)[2]) {
- safe_close_pair(*p);
-}
-
-static inline void fclosep(FILE **f) {
- safe_fclose(*f);
-}
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, pclose);
-DEFINE_TRIVIAL_CLEANUP_FUNC(DIR*, closedir);
-DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, endmntent);
-
-#define _cleanup_free_ _cleanup_(freep)
-#define _cleanup_close_ _cleanup_(closep)
-#define _cleanup_umask_ _cleanup_(umaskp)
-#define _cleanup_globfree_ _cleanup_(globfree)
-#define _cleanup_fclose_ _cleanup_(fclosep)
-#define _cleanup_pclose_ _cleanup_(pclosep)
-#define _cleanup_closedir_ _cleanup_(closedirp)
-#define _cleanup_endmntent_ _cleanup_(endmntentp)
-#define _cleanup_close_pair_ _cleanup_(close_pairp)
-
-_malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t a, size_t b) {
- if (_unlikely_(b != 0 && a > ((size_t) -1) / b))
- return NULL;
-
- return malloc(a * b);
-}
-
-_alloc_(2, 3) static inline void *realloc_multiply(void *p, size_t a, size_t b) {
- if (_unlikely_(b != 0 && a > ((size_t) -1) / b))
- return NULL;
-
- return realloc(p, a * b);
-}
-
-_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t a, size_t b) {
- if (_unlikely_(b != 0 && a > ((size_t) -1) / b))
- return NULL;
-
- return memdup(p, a * b);
-}
-
-bool filename_is_valid(const char *p) _pure_;
-bool path_is_safe(const char *p) _pure_;
-bool string_is_safe(const char *p) _pure_;
-bool string_has_cc(const char *p, const char *ok) _pure_;
-
-/**
- * Check if a string contains any glob patterns.
- */
-_pure_ static inline bool string_is_glob(const char *p) {
- return !!strpbrk(p, GLOB_CHARS);
-}
-
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)
-#define N_(String) String
-void init_gettext(void);
-bool is_locale_utf8(void);
-
-typedef enum DrawSpecialChar {
- DRAW_TREE_VERTICAL,
- DRAW_TREE_BRANCH,
- DRAW_TREE_RIGHT,
- DRAW_TREE_SPACE,
- DRAW_TRIANGULAR_BULLET,
- DRAW_BLACK_CIRCLE,
- DRAW_ARROW,
- DRAW_DASH,
- _DRAW_SPECIAL_CHAR_MAX
-} DrawSpecialChar;
-
-const char *draw_special_char(DrawSpecialChar ch);
-
-char *strreplace(const char *text, const char *old_string, const char *new_string);
+/**
+ * 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, comparison_fn_t compar) {
+ if (nmemb <= 1)
+ return;
-char *strip_tab_ansi(char **p, size_t *l);
+ assert(base);
+ qsort(base, nmemb, size, compar);
+}
int on_ac_power(void);
-int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f);
-int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f);
-
-#define FOREACH_LINE(line, f, on_error) \
- for (;;) \
- if (!fgets(line, sizeof(line), f)) { \
- if (ferror(f)) { \
- on_error; \
- } \
- break; \
- } else
-
-#define FOREACH_DIRENT(de, d, on_error) \
- for (errno = 0, de = readdir(d);; errno = 0, de = readdir(d)) \
- if (!de) { \
- if (errno > 0) { \
- on_error; \
- } \
- break; \
- } else if (hidden_file((de)->d_name)) \
- continue; \
- else
-
-#define FOREACH_DIRENT_ALL(de, d, on_error) \
- for (errno = 0, de = readdir(d);; errno = 0, de = readdir(d)) \
- if (!de) { \
- if (errno > 0) { \
- on_error; \
- } \
- break; \
- } else
+#define memzero(x,l) (memset((x), 0, (l)))
+#define zero(x) (memzero(&(x), sizeof(x)))
static inline void *mempset(void *s, int c, size_t n) {
memset(s, c, n);
return (uint8_t*)s + n;
}
-char *hexmem(const void *p, size_t l);
-int unhexmem(const char *p, size_t l, void **mem, size_t *len);
-
-char *base32hexmem(const void *p, size_t l, bool padding);
-int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *len);
-
-char *base64mem(const void *p, size_t l);
-int unbase64mem(const char *p, size_t l, void **mem, size_t *len);
-
-char *strextend(char **x, ...) _sentinel_;
-char *strrep(const char *s, unsigned n);
-
-void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size);
-void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size);
-#define GREEDY_REALLOC(array, allocated, need) \
- greedy_realloc((void**) &(array), &(allocated), (need), sizeof((array)[0]))
-
-#define GREEDY_REALLOC0(array, allocated, need) \
- greedy_realloc0((void**) &(array), &(allocated), (need), sizeof((array)[0]))
-
static inline void _reset_errno_(int *saved_errno) {
errno = *saved_errno;
}
@@ -673,20 +129,6 @@ static inline int negative_errno(void) {
return -errno;
}
-struct _umask_struct_ {
- mode_t mask;
- bool quit;
-};
-
-static inline void _reset_umask_(struct _umask_struct_ *s) {
- umask(s->mask);
-};
-
-#define RUN_WITH_UMASK(mask) \
- for (_cleanup_(_reset_umask_) struct _umask_struct_ _saved_umask_ = { umask(mask), false }; \
- !_saved_umask_.quit ; \
- _saved_umask_.quit = true)
-
static inline unsigned u64log2(uint64_t n) {
#if __SIZEOF_LONG_LONG__ == 8
return (n > 1) ? (unsigned) __builtin_clzll(n) ^ 63U : 0;
@@ -724,222 +166,15 @@ static inline unsigned log2u_round_up(unsigned x) {
return log2u(x - 1) + 1;
}
-static inline bool logind_running(void) {
- return access("/run/systemd/seats/", F_OK) >= 0;
-}
-
-#define DECIMAL_STR_WIDTH(x) \
- ({ \
- typeof(x) _x_ = (x); \
- unsigned ans = 1; \
- while (_x_ /= 10) \
- ans++; \
- ans; \
- })
-
-int unlink_noerrno(const char *path);
-
-#define alloca0(n) \
- ({ \
- char *_new_; \
- size_t _len_ = n; \
- _new_ = alloca(_len_); \
- (void *) memset(_new_, 0, _len_); \
- })
-
-/* It's not clear what alignment glibc/gcc alloca() guarantee, hence provide a guaranteed safe version */
-#define alloca_align(size, align) \
- ({ \
- void *_ptr_; \
- size_t _mask_ = (align) - 1; \
- _ptr_ = alloca((size) + _mask_); \
- (void*)(((uintptr_t)_ptr_ + _mask_) & ~_mask_); \
- })
-
-#define alloca0_align(size, align) \
- ({ \
- void *_new_; \
- size_t _size_ = (size); \
- _new_ = alloca_align(_size_, (align)); \
- (void*)memset(_new_, 0, _size_); \
- })
-
-#define strjoina(a, ...) \
- ({ \
- const char *_appendees_[] = { a, __VA_ARGS__ }; \
- char *_d_, *_p_; \
- int _len_ = 0; \
- unsigned _i_; \
- for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_) && _appendees_[_i_]; _i_++) \
- _len_ += strlen(_appendees_[_i_]); \
- _p_ = _d_ = alloca(_len_ + 1); \
- for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_) && _appendees_[_i_]; _i_++) \
- _p_ = stpcpy(_p_, _appendees_[_i_]); \
- *_p_ = 0; \
- _d_; \
- })
-
bool id128_is_valid(const char *s) _pure_;
-int split_pair(const char *s, const char *sep, char **l, char **r);
-
-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, 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);
-int parse_proc_cmdline(int (*parse_word)(const char *key, const char *value));
-int get_proc_cmdline_key(const char *parameter, char **value);
-
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 *userns_fd, int *root_fd);
int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd);
-int getpeercred(int fd, struct ucred *ucred);
-int getpeersec(int fd, char **ret);
-
-int writev_safe(int fd, const struct iovec *w, int j);
-
-int mkostemp_safe(char *pattern, int flags);
-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);
-
uint64_t physical_memory(void);
-void hexdump(FILE *f, const void *p, size_t s);
-
-union file_handle_union {
- struct file_handle handle;
- char padding[sizeof(struct file_handle) + MAX_HANDLE_SZ];
-};
-#define FILE_HANDLE_INIT { .handle.handle_bytes = MAX_HANDLE_SZ }
-
int update_reboot_param_file(const char *param);
-int umount_recursive(const char *target, int flags);
-
-int bind_remount_recursive(const char *prefix, bool ro);
-
-int fflush_and_check(FILE *f);
-
-int tempfn_xxxxxx(const char *p, const char *extra, char **ret);
-int tempfn_random(const char *p, const char *extra, char **ret);
-int tempfn_random_child(const char *p, const char *extra, char **ret);
-
-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);
-
-typedef enum ExtractFlags {
- EXTRACT_RELAX = 1,
- EXTRACT_CUNESCAPE = 2,
- EXTRACT_CUNESCAPE_RELAX = 4,
- EXTRACT_QUOTES = 8,
- EXTRACT_DONT_COALESCE_SEPARATORS = 16,
-} ExtractFlags;
-
-int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags);
-int extract_first_word_and_warn(const char **p, char **ret, const char *separators, ExtractFlags flags, const char *unit, const char *filename, unsigned line, const char *rvalue);
-int extract_many_words(const char **p, const char *separators, ExtractFlags flags, ...) _sentinel_;
-
-int free_and_strdup(char **p, const char *s);
-
-#define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX + 1)
-
-#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \
- for ((e) = &buffer.ev; \
- (uint8_t*) (e) < (uint8_t*) (buffer.raw) + (sz); \
- (e) = (struct inotify_event*) ((uint8_t*) (e) + sizeof(struct inotify_event) + (e)->len))
-
-union inotify_event_buffer {
- struct inotify_event ev;
- uint8_t raw[INOTIFY_EVENT_MAX];
-};
-
-#define laccess(path, mode) faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW)
-
-int ptsname_malloc(int fd, char **ret);
-
-int openpt_in_namespace(pid_t pid, int flags);
-
-ssize_t fgetxattrat_fake(int dirfd, const char *filename, const char *attribute, void *value, size_t size, int flags);
-
-int fd_setcrtime(int fd, usec_t usec);
-int fd_getcrtime(int fd, usec_t *usec);
-int path_getcrtime(const char *p, usec_t *usec);
-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, 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);
-
-#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);
-
-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_escape(const char *s, const char *bad);
-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);
-
-int getxattr_malloc(const char *path, const char *name, char **value, bool allow_symlink);
-int fgetxattr_malloc(int fd, const char *name, char **value);
-
-int send_one_fd(int transport_fd, int fd);
-int receive_one_fd(int transport_fd);
+int version(void);
diff --git a/src/basic/verbs.c b/src/basic/verbs.c
index c7beccc2dc..7feb47c48e 100644
--- a/src/basic/verbs.c
+++ b/src/basic/verbs.c
@@ -19,7 +19,14 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
+#include <errno.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+#include "log.h"
+#include "macro.h"
+#include "string-util.h"
#include "verbs.h"
int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) {
diff --git a/src/basic/virt.c b/src/basic/virt.c
index 1fc6c1baba..0ffc2769d2 100644
--- a/src/basic/virt.c
+++ b/src/basic/virt.c
@@ -19,18 +19,26 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <string.h>
#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
#include <unistd.h>
-#include "util.h"
+#include "alloc-util.h"
+#include "dirent-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "macro.h"
#include "process-util.h"
+#include "stat-util.h"
+#include "string-table.h"
+#include "string-util.h"
#include "virt.h"
-#include "fileio.h"
static int detect_vm_cpuid(void) {
- /* Both CPUID and DMI are x86 specific interfaces... */
+ /* CPUID is an x86 specific interface. */
#if defined(__i386__) || defined(__x86_64__)
static const struct {
@@ -140,11 +148,10 @@ static int detect_vm_device_tree(void) {
}
static int detect_vm_dmi(void) {
-
- /* Both CPUID and DMI are x86 specific interfaces... */
-#if defined(__i386__) || defined(__x86_64__)
+#if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
static const char *const dmi_vendors[] = {
+ "/sys/class/dmi/id/product_name", /* Test this before sys_vendor to detect KVM over QEMU */
"/sys/class/dmi/id/sys_vendor",
"/sys/class/dmi/id/board_vendor",
"/sys/class/dmi/id/bios_vendor"
@@ -154,6 +161,7 @@ static int detect_vm_dmi(void) {
const char *vendor;
int id;
} dmi_vendor_table[] = {
+ { "KVM", VIRTUALIZATION_KVM },
{ "QEMU", VIRTUALIZATION_QEMU },
/* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
{ "VMware", VIRTUALIZATION_VMWARE },
@@ -240,7 +248,7 @@ static int detect_vm_zvm(void) {
_cleanup_free_ char *t = NULL;
int r;
- r = get_status_field("/proc/sysinfo", "VM00 Control Program:", &t);
+ r = get_proc_field("/proc/sysinfo", "VM00 Control Program", WHITESPACE, &t);
if (r == -ENOENT)
return VIRTUALIZATION_NONE;
if (r < 0)
@@ -263,24 +271,33 @@ int detect_vm(void) {
if (cached_found >= 0)
return cached_found;
- /* Try xen capabilities file first, if not found try
- * high-level hypervisor sysfs file:
+ /* We have to use the correct order here:
+ * Some virtualization technologies do use KVM hypervisor but are
+ * expected to be detected as something else. So detect DMI first.
*
- * https://bugs.freedesktop.org/show_bug.cgi?id=77271 */
-
- r = detect_vm_xen();
+ * An example is Virtualbox since version 5.0, which uses KVM backend.
+ * Detection via DMI works corretly, the CPU ID would find KVM
+ * only. */
+ r = detect_vm_dmi();
if (r < 0)
return r;
if (r != VIRTUALIZATION_NONE)
goto finish;
- r = detect_vm_dmi();
+ r = detect_vm_cpuid();
if (r < 0)
return r;
if (r != VIRTUALIZATION_NONE)
goto finish;
- r = detect_vm_cpuid();
+ /* x86 xen will most likely be detected by cpuid. If not (most likely
+ * because we're not an x86 guest), then we should try the xen capabilities
+ * file next. If that's not found, then we check for the high-level
+ * hypervisor sysfs file:
+ *
+ * https://bugs.freedesktop.org/show_bug.cgi?id=77271 */
+
+ r = detect_vm_xen();
if (r < 0)
return r;
if (r != VIRTUALIZATION_NONE)
@@ -323,6 +340,7 @@ int detect_container(void) {
{ "lxc-libvirt", VIRTUALIZATION_LXC_LIBVIRT },
{ "systemd-nspawn", VIRTUALIZATION_SYSTEMD_NSPAWN },
{ "docker", VIRTUALIZATION_DOCKER },
+ { "rkt", VIRTUALIZATION_RKT },
};
static thread_local int cached_found = _VIRTUALIZATION_INVALID;
@@ -393,7 +411,7 @@ int detect_container(void) {
goto finish;
}
- r = VIRTUALIZATION_NONE;
+ r = VIRTUALIZATION_CONTAINER_OTHER;
finish:
cached_found = r;
@@ -410,6 +428,16 @@ int detect_virtualization(void) {
return detect_vm();
}
+int running_in_chroot(void) {
+ int ret;
+
+ ret = files_same("/proc/1/root", "/");
+ if (ret < 0)
+ return ret;
+
+ return ret == 0;
+}
+
static const char *const virtualization_table[_VIRTUALIZATION_MAX] = {
[VIRTUALIZATION_NONE] = "none",
[VIRTUALIZATION_KVM] = "kvm",
@@ -429,6 +457,7 @@ static const char *const virtualization_table[_VIRTUALIZATION_MAX] = {
[VIRTUALIZATION_LXC] = "lxc",
[VIRTUALIZATION_OPENVZ] = "openvz",
[VIRTUALIZATION_DOCKER] = "docker",
+ [VIRTUALIZATION_RKT] = "rkt",
[VIRTUALIZATION_CONTAINER_OTHER] = "container-other",
};
diff --git a/src/basic/virt.h b/src/basic/virt.h
index 449e069901..aca961867c 100644
--- a/src/basic/virt.h
+++ b/src/basic/virt.h
@@ -48,6 +48,7 @@ enum {
VIRTUALIZATION_LXC,
VIRTUALIZATION_OPENVZ,
VIRTUALIZATION_DOCKER,
+ VIRTUALIZATION_RKT,
VIRTUALIZATION_CONTAINER_OTHER,
VIRTUALIZATION_CONTAINER_LAST = VIRTUALIZATION_CONTAINER_OTHER,
@@ -67,5 +68,7 @@ int detect_vm(void);
int detect_container(void);
int detect_virtualization(void);
+int running_in_chroot(void);
+
const char *virtualization_to_string(int v) _const_;
int virtualization_from_string(const char *s) _pure_;
diff --git a/src/basic/web-util.c b/src/basic/web-util.c
new file mode 100644
index 0000000000..68ec04021b
--- /dev/null
+++ b/src/basic/web-util.c
@@ -0,0 +1,78 @@
+/*-*- 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 <stdbool.h>
+
+#include "string-util.h"
+#include "utf8.h"
+#include "web-util.h"
+
+bool http_etag_is_valid(const char *etag) {
+ if (isempty(etag))
+ return false;
+
+ if (!endswith(etag, "\""))
+ return false;
+
+ if (!startswith(etag, "\"") && !startswith(etag, "W/\""))
+ return false;
+
+ return true;
+}
+
+bool http_url_is_valid(const char *url) {
+ const char *p;
+
+ if (isempty(url))
+ return false;
+
+ p = startswith(url, "http://");
+ if (!p)
+ p = startswith(url, "https://");
+ if (!p)
+ return false;
+
+ if (isempty(p))
+ return false;
+
+ return ascii_is_valid(p);
+}
+
+bool documentation_url_is_valid(const char *url) {
+ const char *p;
+
+ if (isempty(url))
+ return false;
+
+ if (http_url_is_valid(url))
+ return true;
+
+ p = startswith(url, "file:/");
+ if (!p)
+ p = startswith(url, "info:");
+ if (!p)
+ p = startswith(url, "man:");
+
+ if (isempty(p))
+ return false;
+
+ return ascii_is_valid(p);
+}
diff --git a/src/core/dbus-snapshot.h b/src/basic/web-util.h
index 9288f44e15..40c1509eb8 100644
--- a/src/core/dbus-snapshot.h
+++ b/src/basic/web-util.h
@@ -21,8 +21,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "sd-bus.h"
+#include <stdbool.h>
-extern const sd_bus_vtable bus_snapshot_vtable[];
+#include "macro.h"
-int bus_snapshot_method_remove(sd_bus_message *message, void *userdata, sd_bus_error *error);
+bool http_url_is_valid(const char *url) _pure_;
+
+bool documentation_url_is_valid(const char *url) _pure_;
+
+bool http_etag_is_valid(const char *etag);
diff --git a/src/basic/xattr-util.c b/src/basic/xattr-util.c
new file mode 100644
index 0000000000..960209282f
--- /dev/null
+++ b/src/basic/xattr-util.c
@@ -0,0 +1,202 @@
+/*-*- 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 <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/xattr.h>
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "macro.h"
+#include "sparse-endian.h"
+#include "stdio-util.h"
+#include "time-util.h"
+#include "xattr-util.h"
+
+int getxattr_malloc(const char *path, const char *name, char **value, bool allow_symlink) {
+ char *v;
+ size_t l;
+ ssize_t n;
+
+ assert(path);
+ assert(name);
+ assert(value);
+
+ for (l = 100; ; l = (size_t) n + 1) {
+ v = new0(char, l);
+ if (!v)
+ return -ENOMEM;
+
+ if (allow_symlink)
+ n = lgetxattr(path, name, v, l);
+ else
+ n = getxattr(path, name, v, l);
+
+ if (n >= 0 && (size_t) n < l) {
+ *value = v;
+ return n;
+ }
+
+ free(v);
+
+ if (n < 0 && errno != ERANGE)
+ return -errno;
+
+ if (allow_symlink)
+ n = lgetxattr(path, name, NULL, 0);
+ else
+ n = getxattr(path, name, NULL, 0);
+ if (n < 0)
+ return -errno;
+ }
+}
+
+int fgetxattr_malloc(int fd, const char *name, char **value) {
+ char *v;
+ size_t l;
+ ssize_t n;
+
+ assert(fd >= 0);
+ assert(name);
+ assert(value);
+
+ for (l = 100; ; l = (size_t) n + 1) {
+ v = new0(char, l);
+ if (!v)
+ return -ENOMEM;
+
+ n = fgetxattr(fd, name, v, l);
+
+ if (n >= 0 && (size_t) n < l) {
+ *value = v;
+ return n;
+ }
+
+ free(v);
+
+ if (n < 0 && errno != ERANGE)
+ return -errno;
+
+ n = fgetxattr(fd, name, NULL, 0);
+ if (n < 0)
+ return -errno;
+ }
+}
+
+ssize_t fgetxattrat_fake(int dirfd, const char *filename, const char *attribute, void *value, size_t size, int flags) {
+ char fn[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
+ _cleanup_close_ int fd = -1;
+ ssize_t l;
+
+ /* The kernel doesn't have a fgetxattrat() command, hence let's emulate one */
+
+ fd = openat(dirfd, filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH|(flags & AT_SYMLINK_NOFOLLOW ? O_NOFOLLOW : 0));
+ if (fd < 0)
+ return -errno;
+
+ xsprintf(fn, "/proc/self/fd/%i", fd);
+
+ l = getxattr(fn, attribute, value, size);
+ if (l < 0)
+ return -errno;
+
+ return l;
+}
+
+static int parse_crtime(le64_t le, usec_t *usec) {
+ uint64_t u;
+
+ assert(usec);
+
+ u = le64toh(le);
+ if (u == 0 || u == (uint64_t) -1)
+ return -EIO;
+
+ *usec = (usec_t) u;
+ return 0;
+}
+
+int fd_getcrtime(int fd, usec_t *usec) {
+ le64_t le;
+ ssize_t n;
+
+ assert(fd >= 0);
+ assert(usec);
+
+ /* Until Linux gets a real concept of birthtime/creation time,
+ * let's fake one with xattrs */
+
+ n = fgetxattr(fd, "user.crtime_usec", &le, sizeof(le));
+ if (n < 0)
+ return -errno;
+ if (n != sizeof(le))
+ return -EIO;
+
+ return parse_crtime(le, usec);
+}
+
+int fd_getcrtime_at(int dirfd, const char *name, usec_t *usec, int flags) {
+ le64_t le;
+ ssize_t n;
+
+ n = fgetxattrat_fake(dirfd, name, "user.crtime_usec", &le, sizeof(le), flags);
+ if (n < 0)
+ return -errno;
+ if (n != sizeof(le))
+ return -EIO;
+
+ return parse_crtime(le, usec);
+}
+
+int path_getcrtime(const char *p, usec_t *usec) {
+ le64_t le;
+ ssize_t n;
+
+ assert(p);
+ assert(usec);
+
+ n = getxattr(p, "user.crtime_usec", &le, sizeof(le));
+ if (n < 0)
+ return -errno;
+ if (n != sizeof(le))
+ return -EIO;
+
+ return parse_crtime(le, usec);
+}
+
+int fd_setcrtime(int fd, usec_t usec) {
+ le64_t le;
+
+ assert(fd >= 0);
+
+ if (usec <= 0)
+ usec = now(CLOCK_REALTIME);
+
+ le = htole64((uint64_t) usec);
+ if (fsetxattr(fd, "user.crtime_usec", &le, sizeof(le), 0) < 0)
+ return -errno;
+
+ return 0;
+}
diff --git a/src/core/snapshot.h b/src/basic/xattr-util.h
index f2451b1193..a5134cba89 100644
--- a/src/core/snapshot.h
+++ b/src/basic/xattr-util.h
@@ -21,28 +21,19 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-typedef struct Snapshot Snapshot;
+#include <stdbool.h>
+#include <stddef.h>
+#include <sys/types.h>
+#include "time-util.h"
-typedef enum SnapshotState {
- SNAPSHOT_DEAD,
- SNAPSHOT_ACTIVE,
- _SNAPSHOT_STATE_MAX,
- _SNAPSHOT_STATE_INVALID = -1
-} SnapshotState;
+int getxattr_malloc(const char *path, const char *name, char **value, bool allow_symlink);
+int fgetxattr_malloc(int fd, const char *name, char **value);
-struct Snapshot {
- Unit meta;
+ssize_t fgetxattrat_fake(int dirfd, const char *filename, const char *attribute, void *value, size_t size, int flags);
- SnapshotState state, deserialized_state;
+int fd_setcrtime(int fd, usec_t usec);
- bool cleanup;
-};
-
-extern const UnitVTable snapshot_vtable;
-
-int snapshot_create(Manager *m, const char *name, bool cleanup, sd_bus_error *e, Snapshot **s);
-void snapshot_remove(Snapshot *s);
-
-const char* snapshot_state_to_string(SnapshotState i) _const_;
-SnapshotState snapshot_state_from_string(const char *s) _pure_;
+int fd_getcrtime(int fd, usec_t *usec);
+int path_getcrtime(const char *p, usec_t *usec);
+int fd_getcrtime_at(int dirfd, const char *name, usec_t *usec, int flags);
diff --git a/src/basic/xml.c b/src/basic/xml.c
index 15c629b188..b9976cf5f1 100644
--- a/src/basic/xml.c
+++ b/src/basic/xml.c
@@ -19,9 +19,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
+#include <stddef.h>
#include <string.h>
-#include "util.h"
+#include "macro.h"
+#include "string-util.h"
#include "xml.h"
enum {
diff --git a/src/binfmt/binfmt.c b/src/binfmt/binfmt.c
index 1e216f52bd..03fb413fe5 100644
--- a/src/binfmt/binfmt.c
+++ b/src/binfmt/binfmt.c
@@ -19,22 +19,25 @@
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 <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "alloc-util.h"
+#include "conf-files.h"
+#include "def.h"
+#include "fd-util.h"
+#include "fileio.h"
#include "log.h"
+#include "string-util.h"
#include "strv.h"
#include "util.h"
-#include "conf-files.h"
-#include "fileio.h"
-#include "build.h"
-static const char conf_file_dirs[] = CONF_DIRS_NULSTR("binfmt");
+static const char conf_file_dirs[] = CONF_PATHS_NULSTR("binfmt.d");
static int delete_rule(const char *rule) {
_cleanup_free_ char *x = NULL, *fn = NULL;
@@ -91,8 +94,7 @@ static int apply_file(const char *path, bool ignore_enoent) {
if (feof(f))
break;
- log_error_errno(errno, "Failed to read file '%s', ignoring: %m", path);
- return -errno;
+ return log_error_errno(errno, "Failed to read file '%s', ignoring: %m", path);
}
p = strstrip(l);
@@ -143,9 +145,7 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case '?':
return -EINVAL;
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
index ac1711b318..77eea6aada 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -20,28 +20,32 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
-#include <getopt.h>
-#include <stdlib.h>
#include <assert.h>
-#include <sys/statfs.h>
-#include <sys/stat.h>
-#include <errno.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/mman.h>
-#include <dirent.h>
+#include <blkid/blkid.h>
#include <ctype.h>
-#include <limits.h>
+#include <dirent.h>
+#include <errno.h>
#include <ftw.h>
+#include <getopt.h>
+#include <limits.h>
#include <stdbool.h>
-#include <blkid/blkid.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <unistd.h>
+#include "alloc-util.h"
+#include "blkid-util.h"
#include "efivars.h"
-#include "build.h"
-#include "util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "locale-util.h"
#include "rm-rf.h"
-#include "blkid-util.h"
+#include "string-util.h"
+#include "util.h"
static int verify_esp(const char *p, uint32_t *part, uint64_t *pstart, uint64_t *psize, sd_id128_t *uuid) {
struct statfs sfs;
@@ -266,9 +270,9 @@ static int enumerate_binaries(const char *esp_path, const char *path, const char
if (r < 0)
return r;
if (r > 0)
- printf(" File: └─/%s/%s (%s)\n", path, de->d_name, v);
+ printf(" File: %s/%s/%s (%s)\n", draw_special_char(DRAW_TREE_RIGHT), path, de->d_name, v);
else
- printf(" File: └─/%s/%s\n", path, de->d_name);
+ printf(" File: %s/%s/%s\n", draw_special_char(DRAW_TREE_RIGHT), path, de->d_name);
c++;
}
@@ -320,7 +324,7 @@ static int print_efi_option(uint16_t id, bool in_order) {
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(" File: %s%s\n", draw_special_char(DRAW_TREE_RIGHT), path);
printf("\n");
return 0;
@@ -967,8 +971,7 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- printf(VERSION "\n");
- return 0;
+ return version();
case ARG_PATH:
arg_path = optarg;
diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
index 38b79da886..6d35adc0e2 100644
--- a/src/boot/efi/boot.c
+++ b/src/boot/efi/boot.c
@@ -18,12 +18,12 @@
#include <efi.h>
#include <efilib.h>
-#include "util.h"
#include "console.h"
-#include "graphics.h"
-#include "pefile.h"
#include "disk.h"
+#include "graphics.h"
#include "linux.h"
+#include "pefile.h"
+#include "util.h"
#ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
#define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
diff --git a/src/boot/efi/console.c b/src/boot/efi/console.c
index 66aa88f32e..2151d34432 100644
--- a/src/boot/efi/console.c
+++ b/src/boot/efi/console.c
@@ -18,8 +18,8 @@
#include <efi.h>
#include <efilib.h>
-#include "util.h"
#include "console.h"
+#include "util.h"
#define EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID \
{ 0xdd9e7534, 0x7762, 0x4698, { 0x8c, 0x14, 0xf5, 0x85, 0x17, 0xa6, 0x25, 0xaa } }
diff --git a/src/boot/efi/graphics.c b/src/boot/efi/graphics.c
index f732428216..efa91fa7ac 100644
--- a/src/boot/efi/graphics.c
+++ b/src/boot/efi/graphics.c
@@ -20,8 +20,8 @@
#include <efi.h>
#include <efilib.h>
-#include "util.h"
#include "graphics.h"
+#include "util.h"
EFI_STATUS graphics_mode(BOOLEAN on) {
#define EFI_CONSOLE_CONTROL_PROTOCOL_GUID \
diff --git a/src/boot/efi/linux.c b/src/boot/efi/linux.c
index 809c69310e..e9d097c132 100644
--- a/src/boot/efi/linux.c
+++ b/src/boot/efi/linux.c
@@ -17,8 +17,8 @@
#include <efi.h>
#include <efilib.h>
-#include "util.h"
#include "linux.h"
+#include "util.h"
#define SETUP_MAGIC 0x53726448 /* "HdrS" */
struct SetupHeader {
diff --git a/src/boot/efi/pefile.c b/src/boot/efi/pefile.c
index e6fedbc929..efb3271ee3 100644
--- a/src/boot/efi/pefile.c
+++ b/src/boot/efi/pefile.c
@@ -17,8 +17,8 @@
#include <efi.h>
#include <efilib.h>
-#include "util.h"
#include "pefile.h"
+#include "util.h"
struct DosFileHeader {
UINT8 Magic[2];
diff --git a/src/boot/efi/splash.c b/src/boot/efi/splash.c
index 470ea3e2cc..b584b5e6a9 100644
--- a/src/boot/efi/splash.c
+++ b/src/boot/efi/splash.c
@@ -18,9 +18,9 @@
#include <efi.h>
#include <efilib.h>
-#include "util.h"
#include "graphics.h"
#include "splash.h"
+#include "util.h"
struct bmp_file {
CHAR8 signature[2];
diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c
index 0c5ee4e9ff..2cd5c33cb6 100644
--- a/src/boot/efi/stub.c
+++ b/src/boot/efi/stub.c
@@ -16,12 +16,12 @@
#include <efi.h>
#include <efilib.h>
-#include "util.h"
-#include "pefile.h"
#include "disk.h"
#include "graphics.h"
-#include "splash.h"
#include "linux.h"
+#include "pefile.h"
+#include "splash.h"
+#include "util.h"
/* magic string to find in the binary image */
static const char __attribute__((used)) magic[] = "#### LoaderInfo: systemd-stub " VERSION " ####";
diff --git a/src/bootchart/bootchart.c b/src/bootchart/bootchart.c
index 83ad90c222..6a0e1d6b14 100644
--- a/src/bootchart/bootchart.c
+++ b/src/bootchart/bootchart.c
@@ -33,30 +33,37 @@
***/
-#include <sys/resource.h>
-#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <unistd.h>
+#include <sys/resource.h>
#include <time.h>
-#include <getopt.h>
-#include <limits.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdbool.h>
-#include "systemd/sd-journal.h"
+#include <unistd.h>
-#include "util.h"
+#include "sd-journal.h"
+
+#include "alloc-util.h"
+#include "bootchart.h"
+#include "conf-parser.h"
+#include "def.h"
+#include "fd-util.h"
#include "fileio.h"
+#include "io-util.h"
+#include "list.h"
#include "macro.h"
-#include "conf-parser.h"
-#include "strxcpyx.h"
+#include "parse-util.h"
#include "path-util.h"
#include "store.h"
+#include "string-util.h"
+#include "strxcpyx.h"
#include "svg.h"
-#include "bootchart.h"
-#include "list.h"
+#include "util.h"
static int exiting = 0;
@@ -88,8 +95,6 @@ static void signal_handler(int sig) {
exiting = 1;
}
-#define BOOTCHART_CONF "/etc/systemd/bootchart.conf"
-
#define BOOTCHART_MAX (16*1024*1024)
static void parse_conf(void) {
@@ -110,8 +115,8 @@ static void parse_conf(void) {
{ NULL, NULL, NULL, 0, NULL }
};
- config_parse_many(BOOTCHART_CONF,
- CONF_DIRS_NULSTR("systemd/bootchart.conf"),
+ config_parse_many(PKGSYSCONFDIR "/bootchart.conf",
+ CONF_PATHS_NULSTR("systemd/bootchart.conf.d"),
NULL, config_item_table_lookup, items, true, NULL);
if (init != NULL)
diff --git a/src/bootchart/bootchart.h b/src/bootchart/bootchart.h
index bdb4b00199..8432a2a119 100644
--- a/src/bootchart/bootchart.h
+++ b/src/bootchart/bootchart.h
@@ -25,6 +25,7 @@
***/
#include <stdbool.h>
+
#include "list.h"
#define MAXCPUS 16
diff --git a/src/bootchart/store.c b/src/bootchart/store.c
index caa97b97fc..c1b1e77e44 100644
--- a/src/bootchart/store.c
+++ b/src/bootchart/store.c
@@ -22,22 +22,27 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
-#include <stdlib.h>
+#include <dirent.h>
+#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
-#include <dirent.h>
-#include <fcntl.h>
#include <time.h>
+#include <unistd.h>
-#include "util.h"
-#include "time-util.h"
-#include "strxcpyx.h"
-#include "store.h"
+#include "alloc-util.h"
#include "bootchart.h"
#include "cgroup-util.h"
+#include "dirent-util.h"
+#include "fd-util.h"
#include "fileio.h"
+#include "parse-util.h"
+#include "store.h"
+#include "string-util.h"
+#include "strxcpyx.h"
+#include "time-util.h"
+#include "util.h"
/*
* Alloc a static 4k buffer for stdio - primarily used to increase
diff --git a/src/bootchart/store.h b/src/bootchart/store.h
index bbb4796efd..4d2e0d439f 100644
--- a/src/bootchart/store.h
+++ b/src/bootchart/store.h
@@ -25,6 +25,7 @@
***/
#include <dirent.h>
+
#include "bootchart.h"
double gettime_ns(void);
diff --git a/src/bootchart/svg.c b/src/bootchart/svg.c
index c66f12e3a6..79e261abe5 100644
--- a/src/bootchart/svg.c
+++ b/src/bootchart/svg.c
@@ -22,22 +22,26 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <fcntl.h>
+#include <limits.h>
#include <stdio.h>
#include <string.h>
+#include <sys/utsname.h>
#include <time.h>
-#include <limits.h>
#include <unistd.h>
-#include <sys/utsname.h>
-#include <fcntl.h>
-#include "util.h"
+#include "alloc-util.h"
+#include "architecture.h"
+#include "bootchart.h"
+#include "fd-util.h"
#include "fileio.h"
+#include "list.h"
#include "macro.h"
+#include "stdio-util.h"
#include "store.h"
#include "svg.h"
-#include "bootchart.h"
-#include "list.h"
#include "utf8.h"
+#include "util.h"
#define time_to_graph(t) ((t) * arg_scale_x)
#define ps_to_graph(n) ((n) * arg_scale_y)
@@ -147,7 +151,7 @@ static int svg_title(FILE *of, const char *build, int pscount, double log_start,
_cleanup_free_ char *model = NULL;
_cleanup_free_ char *buf = NULL;
char date[256] = "Unknown";
- char *cpu;
+ const char *cpu;
char *c;
time_t t;
int r;
@@ -168,7 +172,7 @@ static int svg_title(FILE *of, const char *build, int pscount, double log_start,
strncpy(rootbdev, &c[10], sizeof(rootbdev) - 1);
rootbdev[3] = '\0';
- snprintf(filename, sizeof(filename), "/sys/block/%s/device/model", rootbdev);
+ xsprintf(filename, "/sys/block/%s/device/model", rootbdev);
r = read_one_line_file(filename, &model);
if (r < 0)
@@ -188,20 +192,11 @@ static int svg_title(FILE *of, const char *build, int pscount, double log_start,
assert_se(r > 0);
/* CPU type */
- r = read_full_file("/proc/cpuinfo", &buf, NULL);
+ r = get_proc_field("/proc/cpuinfo", PROC_CPUINFO_MODEL, "\n", &buf);
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;
- }
-
- cpu += 13;
- c = strchr(cpu, '\n');
- if (c)
- *c = '\0';
+ cpu = "Unknown";
+ else
+ cpu = buf;
fprintf(of, "<text class=\"t1\" x=\"0\" y=\"30\">Bootchart for %s - %s</text>\n",
uts.nodename, date);
diff --git a/src/bus-proxyd/bus-proxyd.c b/src/bus-proxyd/bus-proxyd.c
index 3cc3b33ae7..6a7134644f 100644
--- a/src/bus-proxyd/bus-proxyd.c
+++ b/src/bus-proxyd/bus-proxyd.c
@@ -22,26 +22,30 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/socket.h>
-#include <unistd.h>
-#include <string.h>
#include <errno.h>
-#include <sys/prctl.h>
-#include <stddef.h>
#include <getopt.h>
#include <pthread.h>
+#include <stddef.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/socket.h>
+#include <unistd.h>
-#include "log.h"
-#include "util.h"
#include "sd-daemon.h"
+
+#include "alloc-util.h"
#include "bus-internal.h"
-#include "build.h"
-#include "strv.h"
-#include "def.h"
-#include "capability.h"
#include "bus-xml-policy.h"
-#include "proxy.h"
+#include "capability-util.h"
+#include "def.h"
+#include "fd-util.h"
#include "formats-util.h"
+#include "log.h"
+#include "proxy.h"
+#include "string-util.h"
+#include "strv.h"
+#include "user-util.h"
+#include "util.h"
static char *arg_address = NULL;
static char **arg_configuration = NULL;
@@ -85,11 +89,11 @@ static void *run_client(void *userdata) {
int r;
r = proxy_new(&p, c->fd, c->fd, arg_address);
+ c->fd = -1;
+
if (r < 0)
goto exit;
- c->fd = -1;
-
/* set comm to "p$PIDu$UID" and suffix with '*' if truncated */
r = snprintf(comm, sizeof(comm), "p" PID_FMT "u" UID_FMT, p->local_creds.pid, p->local_creds.uid);
if (r >= (ssize_t)sizeof(comm))
@@ -116,13 +120,12 @@ static int loop_clients(int accept_fd, uid_t bus_uid) {
int r;
r = pthread_attr_init(&attr);
- if (r < 0) {
- return log_error_errno(errno, "Cannot initialize pthread attributes: %m");
- }
+ if (r != 0)
+ return log_error_errno(r, "Cannot initialize pthread attributes: %m");
r = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
- if (r < 0) {
- r = log_error_errno(errno, "Cannot mark pthread attributes as detached: %m");
+ if (r != 0) {
+ r = log_error_errno(r, "Cannot mark pthread attributes as detached: %m");
goto finish;
}
@@ -156,8 +159,8 @@ static int loop_clients(int accept_fd, uid_t bus_uid) {
c->bus_uid = bus_uid;
r = pthread_create(&tid, &attr, run_client, c);
- if (r < 0) {
- log_error("Cannot spawn thread: %m");
+ if (r != 0) {
+ log_warning_errno(r, "Cannot spawn thread, ignoring: %m");
client_context_free(c);
continue;
}
@@ -215,9 +218,7 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case ARG_ADDRESS:
r = free_and_strdup(&arg_address, optarg);
diff --git a/src/bus-proxyd/bus-xml-policy.c b/src/bus-proxyd/bus-xml-policy.c
index 9a3b451c56..debd58ce8b 100644
--- a/src/bus-proxyd/bus-xml-policy.c
+++ b/src/bus-proxyd/bus-xml-policy.c
@@ -19,15 +19,21 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "xml.h"
-#include "fileio.h"
-#include "strv.h"
-#include "set.h"
-#include "conf-files.h"
+#include "sd-login.h"
+
+#include "alloc-util.h"
#include "bus-internal.h"
#include "bus-xml-policy.h"
-#include "sd-login.h"
+#include "conf-files.h"
+#include "fileio.h"
#include "formats-util.h"
+#include "locale-util.h"
+#include "set.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "strv.h"
+#include "user-util.h"
+#include "xml.h"
static void policy_item_free(PolicyItem *i) {
assert(i);
@@ -386,11 +392,11 @@ static int file_load(Policy *p, const char *path) {
} else {
PolicyItem *first;
- first = hashmap_get(p->user_items, UINT32_TO_PTR(i->uid));
+ first = hashmap_get(p->user_items, UID_TO_PTR(i->uid));
item_append(i, &first);
i->uid_valid = true;
- r = hashmap_replace(p->user_items, UINT32_TO_PTR(i->uid), first);
+ r = hashmap_replace(p->user_items, UID_TO_PTR(i->uid), first);
if (r < 0) {
LIST_REMOVE(items, first, i);
return log_oom();
@@ -418,11 +424,11 @@ static int file_load(Policy *p, const char *path) {
} else {
PolicyItem *first;
- first = hashmap_get(p->group_items, UINT32_TO_PTR(i->gid));
+ first = hashmap_get(p->group_items, GID_TO_PTR(i->gid));
item_append(i, &first);
i->gid_valid = true;
- r = hashmap_replace(p->group_items, UINT32_TO_PTR(i->gid), first);
+ r = hashmap_replace(p->group_items, GID_TO_PTR(i->gid), first);
if (r < 0) {
LIST_REMOVE(items, first, i);
return log_oom();
@@ -781,7 +787,7 @@ static int policy_check(Policy *p, const struct policy_check_filter *filter) {
verdict = check_policy_items(p->default_items, filter);
if (filter->gid != GID_INVALID) {
- items = hashmap_get(p->group_items, UINT32_TO_PTR(filter->gid));
+ items = hashmap_get(p->group_items, GID_TO_PTR(filter->gid));
if (items) {
v = check_policy_items(items, filter);
if (v != DUNNO)
@@ -790,7 +796,7 @@ static int policy_check(Policy *p, const struct policy_check_filter *filter) {
}
if (filter->uid != UID_INVALID) {
- items = hashmap_get(p->user_items, UINT32_TO_PTR(filter->uid));
+ items = hashmap_get(p->user_items, UID_TO_PTR(filter->uid));
if (items) {
v = check_policy_items(items, filter);
if (v != DUNNO)
@@ -1149,7 +1155,7 @@ static void dump_hashmap_items(Hashmap *h) {
void *k;
HASHMAP_FOREACH_KEY(i, k, h, j) {
- printf("\t%s Item for %u:\n", draw_special_char(DRAW_ARROW), PTR_TO_UINT(k));
+ printf("\t%s Item for " UID_FMT ":\n", draw_special_char(DRAW_ARROW), PTR_TO_UID(k));
dump_items(i, "\t\t");
}
}
@@ -1186,14 +1192,14 @@ int shared_policy_new(SharedPolicy **out) {
return log_oom();
r = pthread_mutex_init(&sp->lock, NULL);
- if (r < 0) {
- log_error_errno(r, "Cannot initialize shared policy mutex: %m");
+ if (r != 0) {
+ r = log_error_errno(r, "Cannot initialize shared policy mutex: %m");
goto exit_free;
}
r = pthread_rwlock_init(&sp->rwlock, NULL);
- if (r < 0) {
- log_error_errno(r, "Cannot initialize shared policy rwlock: %m");
+ if (r != 0) {
+ r = log_error_errno(r, "Cannot initialize shared policy rwlock: %m");
goto exit_mutex;
}
diff --git a/src/bus-proxyd/bus-xml-policy.h b/src/bus-proxyd/bus-xml-policy.h
index 8f0ab8f17f..8dde0cb868 100644
--- a/src/bus-proxyd/bus-xml-policy.h
+++ b/src/bus-proxyd/bus-xml-policy.h
@@ -23,8 +23,8 @@
#include <pthread.h>
-#include "list.h"
#include "hashmap.h"
+#include "list.h"
typedef enum PolicyItemType {
_POLICY_ITEM_TYPE_UNSET = 0,
diff --git a/src/bus-proxyd/driver.c b/src/bus-proxyd/driver.c
index fa4aee691a..2849b04252 100644
--- a/src/bus-proxyd/driver.c
+++ b/src/bus-proxyd/driver.c
@@ -21,24 +21,26 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <string.h>
#include <errno.h>
#include <stddef.h>
+#include <string.h>
-#include "util.h"
#include "sd-bus.h"
+
+#include "alloc-util.h"
#include "bus-internal.h"
#include "bus-message.h"
#include "bus-util.h"
-#include "strv.h"
-#include "set.h"
#include "driver.h"
+#include "env-util.h"
#include "proxy.h"
+#include "set.h"
+#include "strv.h"
#include "synthesize.h"
-#include "env-util.h"
+#include "util.h"
static int get_creds_by_name(sd_bus *bus, const char *name, uint64_t mask, sd_bus_creds **_creds, sd_bus_error *error) {
- _cleanup_bus_creds_unref_ sd_bus_creds *c = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL;
int r;
assert(bus);
@@ -73,7 +75,7 @@ static int get_creds_by_message(sd_bus *bus, sd_bus_message *m, uint64_t mask, s
}
static int driver_activation(sd_bus_message *reply, void *userdata, sd_bus_error *error) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
ProxyActivation *activation = userdata;
/*
@@ -237,9 +239,9 @@ int bus_proxy_process_driver(Proxy *p, sd_bus *a, sd_bus *b, sd_bus_message *m,
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;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(sd_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"));
@@ -303,9 +305,9 @@ int bus_proxy_process_driver(Proxy *p, sd_bus *a, sd_bus *b, sd_bus_message *m,
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;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+ _cleanup_(sd_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"));
@@ -328,8 +330,8 @@ int bus_proxy_process_driver(Proxy *p, sd_bus *a, sd_bus *b, sd_bus_message *m,
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;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+ _cleanup_(sd_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"));
@@ -344,8 +346,8 @@ int bus_proxy_process_driver(Proxy *p, sd_bus *a, sd_bus *b, sd_bus_message *m,
return synthetic_reply_method_return(m, "u", (uint32_t) creds->pid);
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "GetConnectionUnixUser")) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+ _cleanup_(sd_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"));
@@ -374,8 +376,8 @@ int bus_proxy_process_driver(Proxy *p, sd_bus *a, sd_bus *b, sd_bus_message *m,
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "GetNameOwner")) {
const char *name;
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+ _cleanup_(sd_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"));
@@ -437,7 +439,7 @@ int bus_proxy_process_driver(Proxy *p, sd_bus *a, sd_bus *b, sd_bus_message *m,
};
struct kdbus_info *name_list, *name;
_cleanup_strv_free_ char **owners = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
char *arg0;
int err = 0;
@@ -608,7 +610,7 @@ int bus_proxy_process_driver(Proxy *p, sd_bus *a, sd_bus *b, sd_bus_message *m,
return synthetic_reply_method_return(m, "u", BUS_NAME_PRIMARY_OWNER);
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "StartServiceByName")) {
- _cleanup_bus_message_unref_ sd_bus_message *msg = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *msg = NULL;
ProxyActivation *activation;
const char *name;
uint64_t cookie;
@@ -672,7 +674,7 @@ int bus_proxy_process_driver(Proxy *p, sd_bus *a, sd_bus *b, sd_bus_message *m,
return 1;
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "UpdateActivationEnvironment")) {
- _cleanup_bus_message_unref_ sd_bus_message *msg = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *msg = NULL;
_cleanup_strv_free_ char **args = NULL;
if (!sd_bus_message_has_signature(m, "a{ss}"))
@@ -736,7 +738,7 @@ int bus_proxy_process_driver(Proxy *p, sd_bus *a, sd_bus *b, sd_bus_message *m,
return synthetic_reply_method_return(m, NULL);
} else {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
r = sd_bus_error_setf(&error, SD_BUS_ERROR_UNKNOWN_METHOD, "Unknown method '%s'.", m->member);
diff --git a/src/bus-proxyd/driver.h b/src/bus-proxyd/driver.h
index da3834f8b0..9f68902441 100644
--- a/src/bus-proxyd/driver.h
+++ b/src/bus-proxyd/driver.h
@@ -22,6 +22,7 @@
***/
#include "sd-bus.h"
+
#include "bus-xml-policy.h"
#include "proxy.h"
diff --git a/src/bus-proxyd/proxy.c b/src/bus-proxyd/proxy.c
index 88800f5e7f..98222c50cc 100644
--- a/src/bus-proxyd/proxy.c
+++ b/src/bus-proxyd/proxy.c
@@ -22,30 +22,34 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <string.h>
#include <errno.h>
#include <poll.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
-#include "log.h"
-#include "util.h"
-#include "sd-daemon.h"
#include "sd-bus.h"
+#include "sd-daemon.h"
+
+#include "alloc-util.h"
+#include "bus-control.h"
#include "bus-internal.h"
#include "bus-message.h"
#include "bus-util.h"
-#include "strv.h"
-#include "bus-control.h"
-#include "set.h"
#include "bus-xml-policy.h"
#include "driver.h"
+#include "fd-util.h"
+#include "formats-util.h"
+#include "log.h"
#include "proxy.h"
+#include "set.h"
+#include "strv.h"
#include "synthesize.h"
-#include "formats-util.h"
+#include "user-util.h"
+#include "util.h"
static int proxy_create_destination(Proxy *p, const char *destination, const char *local_sec, bool negotiate_fds) {
- _cleanup_bus_flush_close_unref_ sd_bus *b = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *b = NULL;
int r;
r = sd_bus_new(&b);
@@ -100,18 +104,24 @@ static int proxy_create_destination(Proxy *p, const char *destination, const cha
return 0;
}
-static int proxy_create_local(Proxy *p, int in_fd, int out_fd, bool negotiate_fds) {
- _cleanup_bus_flush_close_unref_ sd_bus *b = NULL;
+static int proxy_create_local(Proxy *p, bool negotiate_fds) {
sd_id128_t server_id;
+ sd_bus *b;
int r;
r = sd_bus_new(&b);
if (r < 0)
return log_error_errno(r, "Failed to allocate bus: %m");
- r = sd_bus_set_fd(b, in_fd, out_fd);
- if (r < 0)
+ r = sd_bus_set_fd(b, p->local_in, p->local_out);
+ if (r < 0) {
+ sd_bus_unref(b);
return log_error_errno(r, "Failed to set fds: %m");
+ }
+
+ /* The fds are now owned by the bus, and we indicate that by
+ * storing the bus object in the proxy object. */
+ p->local_bus = b;
r = sd_bus_get_bus_id(p->destination_bus, &server_id);
if (r < 0)
@@ -139,8 +149,6 @@ static int proxy_create_local(Proxy *p, int in_fd, int out_fd, bool negotiate_fd
if (r < 0)
return log_error_errno(r, "Failed to start bus client: %m");
- p->local_bus = b;
- b = NULL;
return 0;
}
@@ -224,9 +232,17 @@ int proxy_new(Proxy **out, int in_fd, int out_fd, const char *destination) {
bool is_unix;
int r;
+ /* This takes possession/destroys the file descriptors passed
+ * in even on failure. The caller should hence forget about
+ * the fds in all cases after calling this function and not
+ * close them. */
+
p = new0(Proxy, 1);
- if (!p)
+ if (!p) {
+ safe_close(in_fd);
+ safe_close(out_fd);
return log_oom();
+ }
p->local_in = in_fd;
p->local_out = out_fd;
@@ -247,7 +263,7 @@ int proxy_new(Proxy **out, int in_fd, int out_fd, const char *destination) {
if (r < 0)
return r;
- r = proxy_create_local(p, in_fd, out_fd, is_unix);
+ r = proxy_create_local(p, is_unix);
if (r < 0)
return r;
@@ -257,6 +273,7 @@ int proxy_new(Proxy **out, int in_fd, int out_fd, const char *destination) {
*out = p;
p = NULL;
+
return 0;
}
@@ -273,7 +290,14 @@ Proxy *proxy_free(Proxy *p) {
free(activation);
}
- sd_bus_flush_close_unref(p->local_bus);
+ if (p->local_bus)
+ sd_bus_flush_close_unref(p->local_bus);
+ else {
+ safe_close(p->local_in);
+ if (p->local_out != p->local_in)
+ safe_close(p->local_out);
+ }
+
sd_bus_flush_close_unref(p->destination_bus);
set_free_free(p->owned_names);
free(p);
@@ -467,7 +491,7 @@ static int process_policy_unlocked(sd_bus *from, sd_bus *to, sd_bus_message *m,
(void) sd_bus_creds_get_egid(&m->creds, &sender_gid);
if (sender_uid == UID_INVALID || sender_gid == GID_INVALID) {
- _cleanup_bus_creds_unref_ sd_bus_creds *sender_creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *sender_creds = NULL;
/* If the message came from another legacy
* client, then the message creds will be
@@ -498,7 +522,7 @@ static int process_policy_unlocked(sd_bus *from, sd_bus *to, sd_bus_message *m,
}
if (to->is_kernel) {
- _cleanup_bus_creds_unref_ sd_bus_creds *destination_creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *destination_creds = NULL;
uid_t destination_uid = UID_INVALID;
gid_t destination_gid = GID_INVALID;
const char *destination_unique = NULL;
@@ -585,7 +609,7 @@ static int process_policy(sd_bus *from, sd_bus *to, sd_bus_message *m, SharedPol
}
static int process_hello(Proxy *p, sd_bus_message *m) {
- _cleanup_bus_message_unref_ sd_bus_message *n = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *n = NULL;
bool is_hello;
int r;
@@ -699,7 +723,7 @@ static int patch_sender(sd_bus *a, sd_bus_message *m) {
}
static int proxy_process_destination_to_local(Proxy *p) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
bool matched, matched_synthetic;
int r;
@@ -808,7 +832,7 @@ static int proxy_process_destination_to_local(Proxy *p) {
}
static int proxy_process_local_to_destination(Proxy *p) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
int r;
assert(p);
diff --git a/src/bus-proxyd/proxy.h b/src/bus-proxyd/proxy.h
index 6aac650ac9..7b2e5d422f 100644
--- a/src/bus-proxyd/proxy.h
+++ b/src/bus-proxyd/proxy.h
@@ -22,6 +22,7 @@
***/
#include "sd-bus.h"
+
#include "bus-xml-policy.h"
typedef struct Proxy Proxy;
diff --git a/src/bus-proxyd/stdio-bridge.c b/src/bus-proxyd/stdio-bridge.c
index f275f6705f..636e3f120a 100644
--- a/src/bus-proxyd/stdio-bridge.c
+++ b/src/bus-proxyd/stdio-bridge.c
@@ -21,23 +21,25 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
-#include <string.h>
#include <errno.h>
-#include <stddef.h>
#include <getopt.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
-#include "log.h"
-#include "util.h"
-#include "sd-daemon.h"
#include "sd-bus.h"
+#include "sd-daemon.h"
+
+#include "alloc-util.h"
#include "bus-internal.h"
#include "bus-util.h"
-#include "build.h"
-#include "strv.h"
#include "def.h"
-#include "proxy.h"
#include "formats-util.h"
+#include "log.h"
+#include "proxy.h"
+#include "strv.h"
+#include "user-util.h"
+#include "util.h"
static char *arg_address = NULL;
static char *arg_command_line_buffer = NULL;
@@ -86,9 +88,7 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case ARG_ADDRESS: {
char *a;
@@ -146,7 +146,7 @@ static int parse_argv(int argc, char *argv[]) {
}
static int rename_service(sd_bus *a, sd_bus *b) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
_cleanup_free_ char *p = NULL, *name = NULL;
const char *comm;
char **cmdline;
diff --git a/src/bus-proxyd/synthesize.c b/src/bus-proxyd/synthesize.c
index 15d99103f6..e10f2d69c0 100644
--- a/src/bus-proxyd/synthesize.c
+++ b/src/bus-proxyd/synthesize.c
@@ -23,13 +23,14 @@
#include <stddef.h>
-#include "util.h"
#include "sd-bus.h"
+
#include "bus-internal.h"
+#include "bus-match.h"
#include "bus-message.h"
#include "bus-util.h"
-#include "bus-match.h"
#include "synthesize.h"
+#include "util.h"
int synthetic_driver_send(sd_bus *b, sd_bus_message *m) {
int r;
@@ -49,7 +50,7 @@ int synthetic_driver_send(sd_bus *b, sd_bus_message *m) {
}
int synthetic_reply_method_error(sd_bus_message *call, const sd_bus_error *e) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
int r;
assert(call);
@@ -65,7 +66,7 @@ int synthetic_reply_method_error(sd_bus_message *call, const sd_bus_error *e) {
}
int synthetic_reply_method_errorf(sd_bus_message *call, const char *name, const char *format, ...) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
va_list ap;
va_start(ap, format);
@@ -76,7 +77,7 @@ int synthetic_reply_method_errorf(sd_bus_message *call, const char *name, const
}
int synthetic_reply_method_errno(sd_bus_message *call, int error, const sd_bus_error *p) {
- _cleanup_bus_error_free_ sd_bus_error berror = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error berror = SD_BUS_ERROR_NULL;
assert(call);
@@ -92,7 +93,7 @@ int synthetic_reply_method_errno(sd_bus_message *call, int error, const sd_bus_e
}
int synthetic_reply_method_errnof(sd_bus_message *call, int error, const char *format, ...) {
- _cleanup_bus_error_free_ sd_bus_error berror = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error berror = SD_BUS_ERROR_NULL;
va_list ap;
assert(call);
@@ -108,7 +109,7 @@ int synthetic_reply_method_errnof(sd_bus_message *call, int error, const char *f
}
int synthetic_reply_method_return(sd_bus_message *call, const char *types, ...) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
int r;
assert(call);
@@ -134,7 +135,7 @@ int synthetic_reply_method_return(sd_bus_message *call, const char *types, ...)
}
int synthetic_reply_method_return_strv(sd_bus_message *call, char **l) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
int r;
assert(call);
@@ -154,7 +155,7 @@ int synthetic_reply_method_return_strv(sd_bus_message *call, char **l) {
}
int synthesize_name_acquired(Proxy *p, sd_bus *a, sd_bus *b, sd_bus_message *m) {
- _cleanup_bus_message_unref_ sd_bus_message *n = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *n = NULL;
const char *name, *old_owner, *new_owner;
int r;
diff --git a/src/bus-proxyd/synthesize.h b/src/bus-proxyd/synthesize.h
index b596daddf2..ddfe2fd266 100644
--- a/src/bus-proxyd/synthesize.h
+++ b/src/bus-proxyd/synthesize.h
@@ -22,6 +22,7 @@
***/
#include "sd-bus.h"
+
#include "proxy.h"
int synthetic_driver_send(sd_bus *b, sd_bus_message *m);
diff --git a/src/bus-proxyd/test-bus-xml-policy.c b/src/bus-proxyd/test-bus-xml-policy.c
index d19d0e1b60..1f465edd91 100644
--- a/src/bus-proxyd/test-bus-xml-policy.c
+++ b/src/bus-proxyd/test-bus-xml-policy.c
@@ -19,15 +19,18 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
#include <errno.h>
#include <stddef.h>
+#include <unistd.h>
-#include "log.h"
-#include "util.h"
#include "sd-bus.h"
-#include "strv.h"
+
+#include "alloc-util.h"
#include "bus-xml-policy.h"
+#include "log.h"
+#include "string-util.h"
+#include "strv.h"
+#include "util.h"
static int test_policy_load(Policy *p, const char *name) {
_cleanup_free_ char *path = NULL;
diff --git a/src/cgls/cgls.c b/src/cgls/cgls.c
index ec4215f741..12537ae85b 100644
--- a/src/cgls/cgls.c
+++ b/src/cgls/cgls.c
@@ -19,25 +19,26 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
-#include <unistd.h>
#include <errno.h>
#include <getopt.h>
+#include <stdio.h>
#include <string.h>
+#include <unistd.h>
+
+#include "sd-bus.h"
+#include "alloc-util.h"
+#include "bus-error.h"
+#include "bus-util.h"
#include "cgroup-show.h"
#include "cgroup-util.h"
+#include "fileio.h"
#include "log.h"
-#include "path-util.h"
-#include "util.h"
-#include "pager.h"
-#include "build.h"
#include "output-mode.h"
-#include "fileio.h"
-#include "sd-bus.h"
-#include "bus-util.h"
-#include "bus-error.h"
+#include "pager.h"
+#include "path-util.h"
#include "unit-name.h"
+#include "util.h"
static bool arg_no_pager = false;
static bool arg_kernel_threads = false;
@@ -89,9 +90,7 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case ARG_NO_PAGER:
arg_no_pager = true;
@@ -124,15 +123,17 @@ static int parse_argv(int argc, char *argv[]) {
}
static int get_cgroup_root(char **ret) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_free_ char *unit = NULL, *path = NULL;
const char *m;
int r;
if (!arg_machine) {
r = cg_get_root_path(ret);
- if (r < 0)
+ if (r == -ENOMEDIUM)
+ return log_error_errno(r, "Failed to get root control group path: No cgroup filesystem mounted on /sys/fs/cgroup");
+ else if (r < 0)
return log_error_errno(r, "Failed to get root control group path: %m");
return 0;
@@ -147,7 +148,7 @@ static int get_cgroup_root(char **ret) {
if (!path)
return log_oom();
- r = bus_open_transport(BUS_TRANSPORT_LOCAL, NULL, false, &bus);
+ r = bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL, NULL, false, &bus);
if (r < 0)
return log_error_errno(r, "Failed to create bus connection: %m");
@@ -166,8 +167,10 @@ static int get_cgroup_root(char **ret) {
}
static void show_cg_info(const char *controller, const char *path) {
- if (cg_unified() <= 0)
+
+ if (cg_unified() <= 0 && controller && !streq(controller, SYSTEMD_CGROUP_CONTROLLER))
printf("Controller %s; ", controller);
+
printf("Control group %s:\n", isempty(path) ? "/" : path);
fflush(stdout);
}
@@ -271,6 +274,7 @@ int main(int argc, char *argv[]) {
show_cg_info(SYSTEMD_CGROUP_CONTROLLER, root);
+ printf("-.slice\n");
r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, root, NULL, 0, arg_kernel_threads, output_flags);
}
}
diff --git a/src/cgroups-agent/cgroups-agent.c b/src/cgroups-agent/cgroups-agent.c
index 612bc8fdec..afc95a4a9f 100644
--- a/src/cgroups-agent/cgroups-agent.c
+++ b/src/cgroups-agent/cgroups-agent.c
@@ -22,11 +22,12 @@
#include <stdlib.h>
#include "sd-bus.h"
-#include "log.h"
+
#include "bus-util.h"
+#include "log.h"
int main(int argc, char *argv[]) {
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
if (argc != 2) {
@@ -43,7 +44,7 @@ int main(int argc, char *argv[]) {
* this to avoid an activation loop when we start dbus when we
* are called when the dbus service is shut down. */
- r = bus_open_system_systemd(&bus);
+ r = bus_connect_system_systemd(&bus);
if (r < 0) {
/* If we couldn't connect we assume this was triggered
* while systemd got restarted/transitioned from
diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c
index 9cf13cf57d..4894296554 100644
--- a/src/cgtop/cgtop.c
+++ b/src/cgtop/cgtop.c
@@ -19,27 +19,31 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <errno.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <unistd.h>
#include <alloca.h>
+#include <errno.h>
#include <getopt.h>
#include <signal.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
-#include "path-util.h"
-#include "terminal-util.h"
-#include "process-util.h"
-#include "util.h"
-#include "hashmap.h"
-#include "cgroup-util.h"
-#include "build.h"
-#include "fileio.h"
#include "sd-bus.h"
-#include "bus-util.h"
+
+#include "alloc-util.h"
#include "bus-error.h"
+#include "bus-util.h"
+#include "cgroup-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "hashmap.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "process-util.h"
+#include "stdio-util.h"
+#include "terminal-util.h"
#include "unit-name.h"
+#include "util.h"
typedef struct Group {
char *path;
@@ -562,9 +566,9 @@ static void display(Hashmap *a) {
}
if (arg_cpu_type == CPU_PERCENT)
- snprintf(buffer, sizeof(buffer), "%6s", "%CPU");
+ xsprintf(buffer, "%6s", "%CPU");
else
- snprintf(buffer, sizeof(buffer), "%*s", maxtcpu, "CPU Time");
+ xsprintf(buffer, "%*s", maxtcpu, "CPU Time");
rows = lines();
if (rows <= 10)
@@ -696,9 +700,7 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case ARG_CPU_TYPE:
if (optarg) {
@@ -840,8 +842,8 @@ static const char* counting_what(void) {
}
static int get_cgroup_root(char **ret) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_free_ char *unit = NULL, *path = NULL;
const char *m;
int r;
@@ -863,7 +865,7 @@ static int get_cgroup_root(char **ret) {
if (!path)
return log_oom();
- r = bus_open_transport(BUS_TRANSPORT_LOCAL, NULL, false, &bus);
+ r = bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL, NULL, false, &bus);
if (r < 0)
return log_error_errno(r, "Failed to create bus connection: %m");
diff --git a/src/core/.gitignore b/src/core/.gitignore
index f293bbdc93..465b4fcc20 100644
--- a/src/core/.gitignore
+++ b/src/core/.gitignore
@@ -1,2 +1,3 @@
/macros.systemd
+/triggers.systemd
/systemd.pc
diff --git a/src/core/audit-fd.c b/src/core/audit-fd.c
index 5a18e263a8..0a484d89fc 100644
--- a/src/core/audit-fd.c
+++ b/src/core/audit-fd.c
@@ -21,13 +21,15 @@
#include <errno.h>
+
#include "audit-fd.h"
#ifdef HAVE_AUDIT
-#include <stdbool.h>
#include <libaudit.h>
+#include <stdbool.h>
+#include "fd-util.h"
#include "log.h"
#include "util.h"
diff --git a/src/core/automount.c b/src/core/automount.c
index c88e3311bc..418dbc57c2 100644
--- a/src/core/automount.c
+++ b/src/core/automount.c
@@ -20,29 +20,37 @@
***/
#include <errno.h>
-#include <limits.h>
-#include <sys/mount.h>
-#include <unistd.h>
#include <fcntl.h>
+#include <limits.h>
+#include <linux/auto_dev-ioctl.h>
+#include <linux/auto_fs4.h>
#include <sys/epoll.h>
+#include <sys/mount.h>
#include <sys/stat.h>
-#include <linux/auto_fs4.h>
-#include <linux/auto_dev-ioctl.h>
+#include <unistd.h>
-#include "unit.h"
+#include "alloc-util.h"
+#include "async.h"
#include "automount.h"
-#include "mount.h"
-#include "unit-name.h"
-#include "special.h"
+#include "bus-error.h"
+#include "bus-util.h"
+#include "dbus-automount.h"
+#include "fd-util.h"
+#include "formats-util.h"
+#include "io-util.h"
#include "label.h"
#include "mkdir.h"
+#include "mount-util.h"
+#include "mount.h"
+#include "parse-util.h"
#include "path-util.h"
-#include "dbus-automount.h"
-#include "bus-util.h"
-#include "bus-error.h"
-#include "formats-util.h"
#include "process-util.h"
-#include "async.h"
+#include "special.h"
+#include "stdio-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "unit-name.h"
+#include "unit.h"
static const UnitActiveState state_translation_table[_AUTOMOUNT_STATE_MAX] = {
[AUTOMOUNT_DEAD] = UNIT_INACTIVE,
@@ -81,26 +89,11 @@ static void automount_init(Unit *u) {
UNIT(a)->ignore_on_isolate = true;
}
-static void repeat_unmount(const char *path) {
- assert(path);
-
- for (;;) {
- /* If there are multiple mounts on a mount point, this
- * removes them all */
-
- if (umount2(path, MNT_DETACH) >= 0)
- continue;
-
- if (errno != EINVAL)
- log_error_errno(errno, "Failed to unmount: %m");
-
- break;
- }
-}
-
static int automount_send_ready(Automount *a, Set *tokens, int status);
static void unmount_autofs(Automount *a) {
+ int r;
+
assert(a);
if (a->pipe_fd < 0)
@@ -116,8 +109,11 @@ static void unmount_autofs(Automount *a) {
* around */
if (a->where &&
(UNIT(a)->manager->exit_code != MANAGER_RELOAD &&
- UNIT(a)->manager->exit_code != MANAGER_REEXECUTE))
- repeat_unmount(a->where);
+ UNIT(a)->manager->exit_code != MANAGER_REEXECUTE)) {
+ r = repeat_unmount(a->where, MNT_DETACH);
+ if (r < 0)
+ log_error_errno(r, "Failed to unmount: %m");
+ }
}
static void automount_done(Unit *u) {
@@ -137,13 +133,12 @@ static void automount_done(Unit *u) {
static int automount_add_mount_links(Automount *a) {
_cleanup_free_ char *parent = NULL;
- int r;
assert(a);
- r = path_get_parent(a->where, &parent);
- if (r < 0)
- return r;
+ parent = dirname_malloc(a->where);
+ if (!parent)
+ return -ENOMEM;
return unit_require_mounts_for(UNIT(a), parent);
}
@@ -153,6 +148,9 @@ static int automount_add_default_dependencies(Automount *a) {
assert(a);
+ if (!UNIT(a)->default_dependencies)
+ return 0;
+
if (UNIT(a)->manager->running_as != MANAGER_SYSTEM)
return 0;
@@ -224,11 +222,9 @@ static int automount_load(Unit *u) {
if (r < 0)
return r;
- if (UNIT(a)->default_dependencies) {
- r = automount_add_default_dependencies(a);
- if (r < 0)
- return r;
- }
+ r = automount_add_default_dependencies(a);
+ if (r < 0)
+ return r;
}
return automount_verify(a);
@@ -608,12 +604,16 @@ static void automount_enter_waiting(Automount *a) {
return;
fail:
+ log_unit_error_errno(UNIT(a), r, "Failed to initialize automounter: %m");
+
safe_close_pair(p);
- if (mounted)
- repeat_unmount(a->where);
+ if (mounted) {
+ r = repeat_unmount(a->where, MNT_DETACH);
+ if (r < 0)
+ log_error_errno(r, "Failed to unmount, ignoring: %m");
+ }
- log_unit_error_errno(UNIT(a), r, "Failed to initialize automounter: %m");
automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES);
}
@@ -702,7 +702,7 @@ static int automount_start_expire(Automount *a) {
}
static void automount_enter_runnning(Automount *a) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
struct stat st;
int r;
@@ -728,8 +728,7 @@ static void automount_enter_runnning(Automount *a) {
if (!S_ISDIR(st.st_mode) || st.st_dev != a->dev_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);
+ r = manager_add_job(UNIT(a)->manager, JOB_START, UNIT_TRIGGER(UNIT(a)), JOB_REPLACE, &error, NULL);
if (r < 0) {
log_unit_warning(UNIT(a), "Failed to queue mount startup job: %s", bus_error_message(&error, r));
goto fail;
@@ -774,8 +773,9 @@ static int automount_stop(Unit *u) {
static int automount_serialize(Unit *u, FILE *f, FDSet *fds) {
Automount *a = AUTOMOUNT(u);
- void *p;
Iterator i;
+ void *p;
+ int r;
assert(a);
assert(f);
@@ -790,15 +790,9 @@ static int automount_serialize(Unit *u, FILE *f, FDSet *fds) {
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;
-
- copy = fdset_put_dup(fds, a->pipe_fd);
- if (copy < 0)
- return copy;
-
- unit_serialize_item_format(u, f, "pipe-fd", "%i", copy);
- }
+ r = unit_serialize_item_fd(u, f, fds, "pipe-fd", a->pipe_fd);
+ if (r < 0)
+ return r;
return 0;
}
@@ -903,7 +897,7 @@ 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;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
union autofs_v5_packet_union packet;
Automount *a = AUTOMOUNT(userdata);
struct stat st;
@@ -979,7 +973,7 @@ static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, vo
break;
}
- r = manager_add_job(UNIT(a)->manager, JOB_STOP, UNIT_TRIGGER(UNIT(a)), JOB_REPLACE, true, &error, NULL);
+ r = manager_add_job(UNIT(a)->manager, JOB_STOP, UNIT_TRIGGER(UNIT(a)), JOB_REPLACE, &error, NULL);
if (r < 0) {
log_unit_warning(UNIT(a), "Failed to queue umount startup job: %s", bus_error_message(&error, r));
goto fail;
@@ -1024,15 +1018,6 @@ static bool automount_supported(void) {
return supported;
}
-static const char* const automount_state_table[_AUTOMOUNT_STATE_MAX] = {
- [AUTOMOUNT_DEAD] = "dead",
- [AUTOMOUNT_WAITING] = "waiting",
- [AUTOMOUNT_RUNNING] = "running",
- [AUTOMOUNT_FAILED] = "failed"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(automount_state, AutomountState);
-
static const char* const automount_result_table[_AUTOMOUNT_RESULT_MAX] = {
[AUTOMOUNT_SUCCESS] = "success",
[AUTOMOUNT_FAILURE_RESOURCES] = "resources"
diff --git a/src/core/automount.h b/src/core/automount.h
index 2a50fef68d..43ea9f772d 100644
--- a/src/core/automount.h
+++ b/src/core/automount.h
@@ -25,15 +25,6 @@ typedef struct Automount Automount;
#include "unit.h"
-typedef enum AutomountState {
- AUTOMOUNT_DEAD,
- AUTOMOUNT_WAITING,
- AUTOMOUNT_RUNNING,
- AUTOMOUNT_FAILED,
- _AUTOMOUNT_STATE_MAX,
- _AUTOMOUNT_STATE_INVALID = -1
-} AutomountState;
-
typedef enum AutomountResult {
AUTOMOUNT_SUCCESS,
AUTOMOUNT_FAILURE_RESOURCES,
@@ -66,8 +57,5 @@ extern const UnitVTable automount_vtable;
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_;
-
const char* automount_result_to_string(AutomountResult i) _const_;
AutomountResult automount_result_from_string(const char *s) _pure_;
diff --git a/src/core/bus-endpoint.c b/src/core/bus-endpoint.c
index 0c4b3e7c8b..d22a80c91f 100644
--- a/src/core/bus-endpoint.c
+++ b/src/core/bus-endpoint.c
@@ -19,10 +19,11 @@
#include <stdlib.h>
-#include "kdbus.h"
+#include "alloc-util.h"
+#include "bus-endpoint.h"
#include "bus-kernel.h"
#include "bus-policy.h"
-#include "bus-endpoint.h"
+#include "kdbus.h"
int bus_kernel_set_endpoint_policy(int fd, uid_t uid, BusEndpoint *ep) {
diff --git a/src/core/bus-endpoint.h b/src/core/bus-endpoint.h
index 4a31f4c4be..f6c5f7c5af 100644
--- a/src/core/bus-endpoint.h
+++ b/src/core/bus-endpoint.h
@@ -24,8 +24,8 @@
typedef struct BusEndpoint BusEndpoint;
typedef struct BusEndpointPolicy BusEndpointPolicy;
-#include "hashmap.h"
#include "bus-policy.h"
+#include "hashmap.h"
struct BusEndpointPolicy {
char *name;
diff --git a/src/core/bus-policy.c b/src/core/bus-policy.c
index a6a8fcd4d3..4907c268e8 100644
--- a/src/core/bus-policy.c
+++ b/src/core/bus-policy.c
@@ -19,10 +19,13 @@
#include <stdlib.h>
-#include "kdbus.h"
-#include "util.h"
+#include "alloc-util.h"
#include "bus-kernel.h"
#include "bus-policy.h"
+#include "kdbus.h"
+#include "string-table.h"
+#include "user-util.h"
+#include "util.h"
int bus_kernel_translate_access(BusPolicyAccess access) {
assert(access >= 0);
diff --git a/src/core/bus-policy.h b/src/core/bus-policy.h
index 3b04f5457a..2f61289185 100644
--- a/src/core/bus-policy.h
+++ b/src/core/bus-policy.h
@@ -21,9 +21,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "kdbus.h"
#include "list.h"
#include "macro.h"
-#include "kdbus.h"
typedef struct BusNamePolicy BusNamePolicy;
diff --git a/src/core/busname.c b/src/core/busname.c
index 4020e9dd3c..a949cd6d3f 100644
--- a/src/core/busname.c
+++ b/src/core/busname.c
@@ -21,17 +21,23 @@
#include <sys/mman.h>
-#include "special.h"
-#include "formats-util.h"
-#include "signal-util.h"
-#include "bus-kernel.h"
+#include "alloc-util.h"
#include "bus-internal.h"
+#include "bus-kernel.h"
+#include "bus-policy.h"
#include "bus-util.h"
+#include "busname.h"
+#include "dbus-busname.h"
+#include "fd-util.h"
+#include "formats-util.h"
#include "kdbus.h"
-#include "bus-policy.h"
+#include "parse-util.h"
+#include "process-util.h"
#include "service.h"
-#include "dbus-busname.h"
-#include "busname.h"
+#include "signal-util.h"
+#include "special.h"
+#include "string-table.h"
+#include "string-util.h"
static const UnitActiveState state_translation_table[_BUSNAME_STATE_MAX] = {
[BUSNAME_DEAD] = UNIT_INACTIVE,
@@ -358,10 +364,9 @@ static int busname_coldplug(Unit *u) {
if (n->deserialized_state == n->state)
return 0;
- if (IN_SET(n->deserialized_state, BUSNAME_MAKING, BUSNAME_SIGTERM, BUSNAME_SIGKILL)) {
-
- if (n->control_pid <= 0)
- return -EBADMSG;
+ if (n->control_pid > 0 &&
+ pid_is_unwaited(n->control_pid) &&
+ IN_SET(n->deserialized_state, BUSNAME_MAKING, BUSNAME_SIGTERM, BUSNAME_SIGKILL)) {
r = unit_watch_pid(UNIT(n), n->control_pid);
if (r < 0)
@@ -552,7 +557,7 @@ fail:
}
static void busname_enter_running(BusName *n) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
bool pending = false;
Unit *other;
Iterator i;
@@ -585,7 +590,13 @@ static void busname_enter_running(BusName *n) {
}
if (!pending) {
- r = manager_add_job(UNIT(n)->manager, JOB_START, UNIT_DEREF(n->service), JOB_REPLACE, true, &error, NULL);
+ if (!UNIT_ISSET(n->service)) {
+ log_unit_error(UNIT(n), "Service to activate vanished, refusing activation.");
+ r = -ENOENT;
+ goto fail;
+ }
+
+ r = manager_add_job(UNIT(n)->manager, JOB_START, UNIT_DEREF(n->service), JOB_REPLACE, &error, NULL);
if (r < 0)
goto fail;
}
@@ -656,6 +667,7 @@ static int busname_stop(Unit *u) {
static int busname_serialize(Unit *u, FILE *f, FDSet *fds) {
BusName *n = BUSNAME(u);
+ int r;
assert(n);
assert(f);
@@ -667,15 +679,9 @@ static int busname_serialize(Unit *u, FILE *f, FDSet *fds) {
if (n->control_pid > 0)
unit_serialize_item_format(u, f, "control-pid", PID_FMT, n->control_pid);
- if (n->starter_fd >= 0) {
- int copy;
-
- copy = fdset_put_dup(fds, n->starter_fd);
- if (copy < 0)
- return copy;
-
- unit_serialize_item_format(u, f, "starter-fd", "%i", copy);
- }
+ r = unit_serialize_item_fd(u, f, fds, "starter-fd", n->starter_fd);
+ if (r < 0)
+ return r;
return 0;
}
@@ -991,19 +997,6 @@ static bool busname_supported(void) {
return supported;
}
-static const char* const busname_state_table[_BUSNAME_STATE_MAX] = {
- [BUSNAME_DEAD] = "dead",
- [BUSNAME_MAKING] = "making",
- [BUSNAME_REGISTERED] = "registered",
- [BUSNAME_LISTENING] = "listening",
- [BUSNAME_RUNNING] = "running",
- [BUSNAME_SIGTERM] = "sigterm",
- [BUSNAME_SIGKILL] = "sigkill",
- [BUSNAME_FAILED] = "failed",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(busname_state, BusNameState);
-
static const char* const busname_result_table[_BUSNAME_RESULT_MAX] = {
[BUSNAME_SUCCESS] = "success",
[BUSNAME_FAILURE_RESOURCES] = "resources",
diff --git a/src/core/busname.h b/src/core/busname.h
index 69528a2aef..46f7b6f097 100644
--- a/src/core/busname.h
+++ b/src/core/busname.h
@@ -24,19 +24,7 @@
typedef struct BusName BusName;
typedef struct BusNamePolicy BusNamePolicy;
-
-typedef enum BusNameState {
- BUSNAME_DEAD,
- BUSNAME_MAKING,
- BUSNAME_REGISTERED,
- BUSNAME_LISTENING,
- BUSNAME_RUNNING,
- BUSNAME_SIGTERM,
- BUSNAME_SIGKILL,
- BUSNAME_FAILED,
- _BUSNAME_STATE_MAX,
- _BUSNAME_STATE_INVALID = -1
-} BusNameState;
+#include "unit.h"
typedef enum BusNameResult {
BUSNAME_SUCCESS,
@@ -77,8 +65,5 @@ struct BusName {
extern const UnitVTable busname_vtable;
-const char* busname_state_to_string(BusNameState i) _const_;
-BusNameState busname_state_from_string(const char *s) _pure_;
-
const char* busname_result_to_string(BusNameResult i) _const_;
BusNameResult busname_result_from_string(const char *s) _pure_;
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index 0c790c33da..d122175417 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -22,12 +22,18 @@
#include <fcntl.h>
#include <fnmatch.h>
+#include "alloc-util.h"
#include "cgroup-util.h"
+#include "cgroup.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
#include "special.h"
-
-#include "cgroup.h"
+#include "string-table.h"
+#include "string-util.h"
#define CGROUP_CPU_QUOTA_PERIOD_USEC ((usec_t) 100 * USEC_PER_MSEC)
@@ -211,7 +217,7 @@ static int whitelist_device(const char *path, const char *node, const char *acc)
r = cg_set_attribute("devices", path, "devices.allow", buf);
if (r < 0)
- log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL) ? LOG_DEBUG : LOG_WARNING, r,
+ log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to set devices.allow on %s: %m", path);
return r;
@@ -282,7 +288,7 @@ static int whitelist_major(const char *path, const char *name, char type, const
r = cg_set_attribute("devices", path, "devices.allow", buf);
if (r < 0)
- log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL) ? LOG_DEBUG : LOG_WARNING, r,
+ log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to set devices.allow on %s: %m", path);
}
@@ -322,13 +328,13 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, u
c->cpu_shares != CGROUP_CPU_SHARES_INVALID ? c->cpu_shares : CGROUP_CPU_SHARES_DEFAULT);
r = cg_set_attribute("cpu", path, "cpu.shares", buf);
if (r < 0)
- log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r,
+ log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to set cpu.shares on %s: %m", path);
sprintf(buf, USEC_FMT "\n", CGROUP_CPU_QUOTA_PERIOD_USEC);
r = cg_set_attribute("cpu", path, "cpu.cfs_period_us", buf);
if (r < 0)
- log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r,
+ log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to set cpu.cfs_period_us on %s: %m", path);
if (c->cpu_quota_per_sec_usec != USEC_INFINITY) {
@@ -337,7 +343,7 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, u
} else
r = cg_set_attribute("cpu", path, "cpu.cfs_quota_us", "-1");
if (r < 0)
- log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r,
+ log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to set cpu.cfs_quota_us on %s: %m", path);
}
@@ -353,7 +359,7 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, u
c->blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID ? c->blockio_weight : CGROUP_BLKIO_WEIGHT_DEFAULT);
r = cg_set_attribute("blkio", path, "blkio.weight", buf);
if (r < 0)
- log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r,
+ log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to set blkio.weight on %s: %m", path);
/* FIXME: no way to reset this list */
@@ -367,7 +373,7 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, u
sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), w->weight);
r = cg_set_attribute("blkio", path, "blkio.weight_device", buf);
if (r < 0)
- log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r,
+ log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to set blkio.weight_device on %s: %m", path);
}
}
@@ -386,7 +392,7 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, u
sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), b->bandwidth);
r = cg_set_attribute("blkio", path, a, buf);
if (r < 0)
- log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r,
+ log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to set %s on %s: %m", a, path);
}
}
@@ -410,7 +416,7 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, u
}
if (r < 0)
- log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r,
+ log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to set memory.limit_in_bytes/memory.max on %s: %m", path);
}
@@ -426,7 +432,7 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, u
else
r = cg_set_attribute("devices", path, "devices.allow", "a");
if (r < 0)
- log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL) ? LOG_DEBUG : LOG_WARNING, r,
+ log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to reset devices.list on %s: %m", path);
if (c->device_policy == CGROUP_CLOSED ||
@@ -488,7 +494,7 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, u
r = cg_set_attribute("pids", path, "pids.max", "max");
if (r < 0)
- log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r,
+ log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to set pids.max on %s: %m", path);
}
@@ -499,7 +505,7 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, u
r = cg_set_attribute("net_cls", path, "net_cls.classid", buf);
if (r < 0)
- log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r,
+ log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to set net_cls.classid on %s: %m", path);
}
}
@@ -1203,7 +1209,7 @@ int unit_search_main_pid(Unit *u, pid_t *ret) {
continue;
/* Ignore processes that aren't our kids */
- if (get_parent_of_pid(npid, &ppid) >= 0 && ppid != mypid)
+ if (get_process_ppid(npid, &ppid) >= 0 && ppid != mypid)
continue;
if (pid != 0)
diff --git a/src/core/cgroup.h b/src/core/cgroup.h
index 457544b49f..1b18d06652 100644
--- a/src/core/cgroup.h
+++ b/src/core/cgroup.h
@@ -112,8 +112,8 @@ struct CGroupContext {
bool delegate;
};
-#include "unit.h"
#include "cgroup-util.h"
+#include "unit.h"
void cgroup_context_init(CGroupContext *c);
void cgroup_context_done(CGroupContext *c);
diff --git a/src/core/dbus-automount.c b/src/core/dbus-automount.c
index 5162ce34cb..54830a515b 100644
--- a/src/core/dbus-automount.c
+++ b/src/core/dbus-automount.c
@@ -20,8 +20,9 @@
***/
#include "automount.h"
-#include "dbus-automount.h"
#include "bus-util.h"
+#include "dbus-automount.h"
+#include "string-util.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, automount_result, AutomountResult);
diff --git a/src/core/dbus-busname.c b/src/core/dbus-busname.c
index b1ceb05b1a..445b237643 100644
--- a/src/core/dbus-busname.c
+++ b/src/core/dbus-busname.c
@@ -19,10 +19,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "unit.h"
+#include "bus-util.h"
#include "busname.h"
#include "dbus-busname.h"
-#include "bus-util.h"
+#include "string-util.h"
+#include "unit.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, busname_result, BusNameResult);
diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c
index f334dc928d..3fd295baa9 100644
--- a/src/core/dbus-cgroup.c
+++ b/src/core/dbus-cgroup.c
@@ -19,11 +19,14 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "alloc-util.h"
#include "bus-util.h"
-#include "path-util.h"
#include "cgroup-util.h"
#include "cgroup.h"
#include "dbus-cgroup.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "path-util.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_cgroup_device_policy, cgroup_device_policy, CGroupDevicePolicy);
@@ -421,7 +424,9 @@ int bus_cgroup_set_property(
fprintf(f, "BlockIOWriteBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth);
}
- fflush(f);
+ r = fflush_and_check(f);
+ if (r < 0)
+ return r;
unit_write_drop_in_private(u, mode, name, buf);
}
@@ -495,7 +500,9 @@ int bus_cgroup_set_property(
LIST_FOREACH(device_weights, a, c->blockio_device_weights)
fprintf(f, "BlockIODeviceWeight=%s %" PRIu64 "\n", a->path, a->weight);
- fflush(f);
+ r = fflush_and_check(f);
+ if (r < 0)
+ return r;
unit_write_drop_in_private(u, mode, name, buf);
}
@@ -640,7 +647,9 @@ int bus_cgroup_set_property(
LIST_FOREACH(device_allow, a, c->device_allow)
fprintf(f, "DeviceAllow=%s %s%s%s\n", a->path, a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "");
- fflush(f);
+ r = fflush_and_check(f);
+ if (r < 0)
+ return r;
unit_write_drop_in_private(u, mode, name, buf);
}
diff --git a/src/core/dbus-cgroup.h b/src/core/dbus-cgroup.h
index c2a3910f3d..9dc187c066 100644
--- a/src/core/dbus-cgroup.h
+++ b/src/core/dbus-cgroup.h
@@ -22,6 +22,7 @@
***/
#include "sd-bus.h"
+
#include "cgroup.h"
extern const sd_bus_vtable bus_cgroup_vtable[];
diff --git a/src/core/dbus-device.c b/src/core/dbus-device.c
index cb156fd37c..97e4a47556 100644
--- a/src/core/dbus-device.c
+++ b/src/core/dbus-device.c
@@ -19,9 +19,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "unit.h"
-#include "device.h"
#include "dbus-device.h"
+#include "device.h"
+#include "unit.h"
const sd_bus_vtable bus_device_vtable[] = {
SD_BUS_VTABLE_START(0),
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
index 868c8cc05a..eae0808f9e 100644
--- a/src/core/dbus-execute.c
+++ b/src/core/dbus-execute.c
@@ -25,22 +25,28 @@
#include <seccomp.h>
#endif
+#include "af-list.h"
+#include "alloc-util.h"
#include "bus-util.h"
-#include "missing.h"
-#include "ioprio.h"
-#include "strv.h"
-#include "fileio.h"
-#include "execute.h"
-#include "capability.h"
+#include "capability-util.h"
+#include "dbus-execute.h"
#include "env-util.h"
-#include "af-list.h"
+#include "execute.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "ioprio.h"
+#include "missing.h"
#include "namespace.h"
+#include "parse-util.h"
#include "path-util.h"
-#include "dbus-execute.h"
-
+#include "process-util.h"
+#include "rlimit-util.h"
#ifdef HAVE_SECCOMP
#include "seccomp-util.h"
#endif
+#include "strv.h"
+#include "syslog-util.h"
+#include "utf8.h"
BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_exec_output, exec_output, ExecOutput);
@@ -83,45 +89,6 @@ static int property_get_environment_files(
return sd_bus_message_close_container(reply);
}
-static int property_get_rlimit(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *error) {
-
- struct rlimit *rl;
- uint64_t u;
- rlim_t x;
-
- assert(bus);
- assert(reply);
- assert(userdata);
-
- rl = *(struct rlimit**) userdata;
- if (rl)
- x = rl->rlim_max;
- else {
- struct rlimit buf = {};
- int z;
-
- z = rlimit_from_string(property);
- assert(z >= 0);
-
- getrlimit(z, &buf);
- x = buf.rlim_max;
- }
-
- /* rlim_t might have different sizes, let's map
- * RLIMIT_INFINITY to (uint64_t) -1, so that it is the same on
- * all archs */
- u = x == RLIM_INFINITY ? (uint64_t) -1 : (uint64_t) x;
-
- return sd_bus_message_append(reply, "t", u);
-}
-
static int property_get_oom_score_adjust(
sd_bus *bus,
const char *path,
@@ -146,7 +113,7 @@ static int property_get_oom_score_adjust(
n = 0;
if (read_one_line_file("/proc/self/oom_score_adj", &t) >= 0)
- safe_atoi(t, &n);
+ safe_atoi32(t, &n);
}
return sd_bus_message_append(reply, "i", n);
@@ -174,7 +141,7 @@ static int property_get_nice(
else {
errno = 0;
n = getpriority(PRIO_PROCESS, 0);
- if (errno != 0)
+ if (errno > 0)
n = 0;
}
@@ -326,9 +293,25 @@ static int property_get_capability_bounding_set(
assert(reply);
assert(c);
- /* We store this negated internally, to match the kernel, but
- * we expose it normalized. */
- return sd_bus_message_append(reply, "t", ~c->capability_bounding_set_drop);
+ return sd_bus_message_append(reply, "t", c->capability_bounding_set);
+}
+
+static int property_get_ambient_capabilities(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ ExecContext *c = userdata;
+
+ assert(bus);
+ assert(reply);
+ assert(c);
+
+ return sd_bus_message_append(reply, "t", c->capability_ambient_set);
}
static int property_get_capabilities(
@@ -595,28 +578,108 @@ static int property_get_address_families(
return sd_bus_message_close_container(reply);
}
+static int property_get_working_directory(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ ExecContext *c = userdata;
+ const char *wd;
+
+ assert(bus);
+ assert(reply);
+ assert(c);
+
+ if (c->working_directory_home)
+ wd = "~";
+ else
+ wd = c->working_directory;
+
+ if (c->working_directory_missing_ok)
+ wd = strjoina("!", wd);
+
+ return sd_bus_message_append(reply, "s", wd);
+}
+
+static int property_get_syslog_level(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ ExecContext *c = userdata;
+
+ assert(bus);
+ assert(reply);
+ assert(c);
+
+ return sd_bus_message_append(reply, "i", LOG_PRI(c->syslog_priority));
+}
+
+static int property_get_syslog_facility(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ ExecContext *c = userdata;
+
+ assert(bus);
+ assert(reply);
+ assert(c);
+
+ return sd_bus_message_append(reply, "i", LOG_FAC(c->syslog_priority));
+}
+
const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(ExecContext, environment), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("EnvironmentFiles", "a(sb)", property_get_environment_files, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("PassEnvironment", "as", NULL, offsetof(ExecContext, pass_environment), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("UMask", "u", bus_property_get_mode, offsetof(ExecContext, umask), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("LimitCPU", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CPU]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("LimitFSIZE", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_FSIZE]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("LimitDATA", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_DATA]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("LimitSTACK", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_STACK]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("LimitCORE", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CORE]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("LimitRSS", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RSS]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("LimitNOFILE", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NOFILE]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("LimitAS", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_AS]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("LimitNPROC", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NPROC]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("LimitMEMLOCK", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_MEMLOCK]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("LimitLOCKS", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_LOCKS]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("LimitSIGPENDING", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_SIGPENDING]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("LimitMSGQUEUE", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_MSGQUEUE]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("LimitNICE", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NICE]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("LimitRTPRIO", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTPRIO]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("LimitRTTIME", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTTIME]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("WorkingDirectory", "s", NULL, offsetof(ExecContext, working_directory), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitCPU", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CPU]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitCPUSoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CPU]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitFSIZE", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_FSIZE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitFSIZESoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_FSIZE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitDATA", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_DATA]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitDATASoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_DATA]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitSTACK", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_STACK]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitSTACKSoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_STACK]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitCORE", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CORE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitCORESoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CORE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitRSS", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RSS]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitRSSSoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RSS]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitNOFILE", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NOFILE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitNOFILESoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NOFILE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitAS", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_AS]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitASSoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_AS]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitNPROC", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NPROC]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitNPROCSoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NPROC]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitMEMLOCK", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_MEMLOCK]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitMEMLOCKSoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_MEMLOCK]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitLOCKS", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_LOCKS]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitLOCKSSoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_LOCKS]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitSIGPENDING", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_SIGPENDING]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitSIGPENDINGSoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_SIGPENDING]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitMSGQUEUE", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_MSGQUEUE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitMSGQUEUESoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_MSGQUEUE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitNICE", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NICE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitNICESoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NICE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitRTPRIO", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTPRIO]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitRTPRIOSoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTPRIO]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitRTTIME", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTTIME]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitRTTIMESoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTTIME]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("WorkingDirectory", "s", property_get_working_directory, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(ExecContext, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("OOMScoreAdjust", "i", property_get_oom_score_adjust, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Nice", "i", property_get_nice, 0, SD_BUS_VTABLE_PROPERTY_CONST),
@@ -637,9 +700,12 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("SyslogPriority", "i", bus_property_get_int, offsetof(ExecContext, syslog_priority), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SyslogIdentifier", "s", NULL, offsetof(ExecContext, syslog_identifier), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SyslogLevelPrefix", "b", bus_property_get_bool, offsetof(ExecContext, syslog_level_prefix), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("SyslogLevel", "i", property_get_syslog_level, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("SyslogFacility", "i", property_get_syslog_facility, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Capabilities", "s", property_get_capabilities, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SecureBits", "i", bus_property_get_int, offsetof(ExecContext, secure_bits), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("CapabilityBoundingSet", "t", property_get_capability_bounding_set, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("AmbientCapabilities", "t", property_get_ambient_capabilities, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("User", "s", NULL, offsetof(ExecContext, user), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Group", "s", NULL, offsetof(ExecContext, group), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SupplementaryGroups", "as", NULL, offsetof(ExecContext, supplementary_groups), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -829,7 +895,64 @@ int bus_exec_context_set_transient_property(
}
return 1;
+ } else if (streq(name, "SyslogIdentifier")) {
+ const char *id;
+
+ r = sd_bus_message_read(message, "s", &id);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+
+ if (isempty(id)) {
+ c->syslog_identifier = mfree(c->syslog_identifier);
+ } else {
+ char *t;
+
+ t = strdup(id);
+ if (!t)
+ return -ENOMEM;
+ free(c->syslog_identifier);
+ c->syslog_identifier = t;
+ }
+
+ unit_write_drop_in_private_format(u, mode, name, "SyslogIdentifier=%s\n", id);
+ }
+
+ return 1;
+ } else if (streq(name, "SyslogLevel")) {
+ int level;
+
+ r = sd_bus_message_read(message, "i", &level);
+ if (r < 0)
+ return r;
+
+ if (!log_level_is_valid(level))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Log level value out of range");
+
+ if (mode != UNIT_CHECK) {
+ c->syslog_priority = (c->syslog_priority & LOG_FACMASK) | level;
+ unit_write_drop_in_private_format(u, mode, name, "SyslogLevel=%i\n", level);
+ }
+
+ return 1;
+ } else if (streq(name, "SyslogFacility")) {
+ int facility;
+
+ r = sd_bus_message_read(message, "i", &facility);
+ if (r < 0)
+ return r;
+
+ if (!log_facility_unshifted_is_valid(facility))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Log facility value out of range");
+
+ if (mode != UNIT_CHECK) {
+ c->syslog_priority = (facility << 3) | LOG_PRI(c->syslog_priority);
+ unit_write_drop_in_private_format(u, mode, name, "SyslogFacility=%i\n", facility);
+ }
+
+ return 1;
} else if (streq(name, "Nice")) {
int n;
@@ -847,8 +970,7 @@ int bus_exec_context_set_transient_property(
return 1;
- } else if (STR_IN_SET(name,
- "TTYPath", "WorkingDirectory", "RootDirectory")) {
+ } else if (STR_IN_SET(name, "TTYPath", "RootDirectory")) {
const char *s;
r = sd_bus_message_read(message, "s", &s);
@@ -859,24 +981,51 @@ int bus_exec_context_set_transient_property(
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s takes an absolute path", name);
if (mode != UNIT_CHECK) {
- char *t;
+ if (streq(name, "TTYPath"))
+ r = free_and_strdup(&c->tty_path, s);
+ else {
+ assert(streq(name, "RootDirectory"));
+ r = free_and_strdup(&c->root_directory, s);
+ }
+ if (r < 0)
+ return r;
- t = strdup(s);
- if (!t)
- return -ENOMEM;
+ unit_write_drop_in_private_format(u, mode, name, "%s=%s\n", name, s);
+ }
- if (streq(name, "TTYPath")) {
- free(c->tty_path);
- c->tty_path = t;
- } else if (streq(name, "WorkingDirectory")) {
- free(c->working_directory);
- c->working_directory = t;
- } else if (streq(name, "RootDirectory")) {
- free(c->root_directory);
- c->root_directory = t;
+ return 1;
+
+ } else if (streq(name, "WorkingDirectory")) {
+ const char *s;
+ bool missing_ok;
+
+ r = sd_bus_message_read(message, "s", &s);
+ if (r < 0)
+ return r;
+
+ if (s[0] == '-') {
+ missing_ok = true;
+ s++;
+ } else
+ missing_ok = false;
+
+ if (!streq(s, "~") && !path_is_absolute(s))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "WorkingDirectory= expects an absolute path or '~'");
+
+ if (mode != UNIT_CHECK) {
+ if (streq(s, "~")) {
+ c->working_directory = mfree(c->working_directory);
+ c->working_directory_home = true;
+ } else {
+ r = free_and_strdup(&c->working_directory, s);
+ if (r < 0)
+ return r;
+
+ c->working_directory_home = false;
}
- unit_write_drop_in_private_format(u, mode, name, "%s=%s\n", name, s);
+ c->working_directory_missing_ok = missing_ok;
+ unit_write_drop_in_private_format(u, mode, name, "WorkingDirectory=%s%s", missing_ok ? "-" : "", s);
}
return 1;
@@ -945,7 +1094,7 @@ int bus_exec_context_set_transient_property(
} else if (STR_IN_SET(name,
"IgnoreSIGPIPE", "TTYVHangup", "TTYReset",
"PrivateTmp", "PrivateDevices", "PrivateNetwork",
- "NoNewPrivileges")) {
+ "NoNewPrivileges", "SyslogLevelPrefix")) {
int b;
r = sd_bus_message_read(message, "b", &b);
@@ -967,6 +1116,8 @@ int bus_exec_context_set_transient_property(
c->private_network = b;
else if (streq(name, "NoNewPrivileges"))
c->no_new_privileges = b;
+ else if (streq(name, "SyslogLevelPrefix"))
+ c->syslog_level_prefix = b;
unit_write_drop_in_private_format(u, mode, name, "%s=%s\n", name, yes_no(b));
}
@@ -1044,18 +1195,299 @@ int bus_exec_context_set_transient_property(
_cleanup_free_ char *joined = NULL;
char **e;
- e = strv_env_merge(2, c->environment, l);
- if (!e)
- return -ENOMEM;
+ if (strv_length(l) == 0) {
+ c->environment = strv_free(c->environment);
+ unit_write_drop_in_private_format(u, mode, name, "Environment=\n");
+ } else {
+ e = strv_env_merge(2, c->environment, l);
+ if (!e)
+ return -ENOMEM;
- strv_free(c->environment);
- c->environment = e;
+ strv_free(c->environment);
+ c->environment = e;
- joined = strv_join_quoted(c->environment);
- if (!joined)
- return -ENOMEM;
+ joined = strv_join_quoted(c->environment);
+ if (!joined)
+ return -ENOMEM;
+
+ unit_write_drop_in_private_format(u, mode, name, "Environment=%s\n", joined);
+ }
+ }
+
+ return 1;
+
+ } else if (streq(name, "TimerSlackNSec")) {
+
+ nsec_t n;
+
+ r = sd_bus_message_read(message, "t", &n);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ c->timer_slack_nsec = n;
+ unit_write_drop_in_private_format(u, mode, name, "TimerSlackNSec=" NSEC_FMT "\n", n);
+ }
+
+ return 1;
+
+ } else if (streq(name, "OOMScoreAdjust")) {
+ int oa;
+
+ r = sd_bus_message_read(message, "i", &oa);
+ if (r < 0)
+ return r;
+
+ if (!oom_score_adjust_is_valid(oa))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "OOM score adjust value out of range");
+
+ if (mode != UNIT_CHECK) {
+ c->oom_score_adjust = oa;
+ c->oom_score_adjust_set = true;
+ unit_write_drop_in_private_format(u, mode, name, "OOMScoreAdjust=%i\n", oa);
+ }
+
+ return 1;
+
+ } else if (streq(name, "EnvironmentFiles")) {
+
+ _cleanup_free_ char *joined = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_free_ char **l = NULL;
+ size_t size = 0;
+ char **i;
+
+ r = sd_bus_message_enter_container(message, 'a', "(sb)");
+ if (r < 0)
+ return r;
+
+ f = open_memstream(&joined, &size);
+ if (!f)
+ return -ENOMEM;
+
+ STRV_FOREACH(i, c->environment_files)
+ fprintf(f, "EnvironmentFile=%s\n", *i);
+
+ while ((r = sd_bus_message_enter_container(message, 'r', "sb")) > 0) {
+ const char *path;
+ int b;
+
+ r = sd_bus_message_read(message, "sb", &path, &b);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ if (!isempty(path) && !path_is_absolute(path))
+ return sd_bus_error_set_errnof(error, EINVAL, "Path %s is not absolute.", path);
+
+ if (mode != UNIT_CHECK) {
+ char *buf = NULL;
+
+ buf = strjoin(b ? "-" : "", path, NULL);
+ if (!buf)
+ return -ENOMEM;
+
+ fprintf(f, "EnvironmentFile=%s\n", buf);
+
+ r = strv_consume(&l, buf);
+ if (r < 0)
+ return r;
+ }
+ }
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ r = fflush_and_check(f);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ if (strv_isempty(l)) {
+ c->environment_files = strv_free(c->environment_files);
+ unit_write_drop_in_private(u, mode, name, "EnvironmentFile=\n");
+ } else {
+ r = strv_extend_strv(&c->environment_files, l, true);
+ if (r < 0)
+ return r;
+
+ unit_write_drop_in_private(u, mode, name, joined);
+ }
+ }
+
+ return 1;
+
+ } else if (streq(name, "PassEnvironment")) {
+
+ _cleanup_strv_free_ char **l = NULL;
+
+ r = sd_bus_message_read_strv(message, &l);
+ if (r < 0)
+ return r;
+
+ if (!strv_env_name_is_valid(l))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid PassEnvironment block.");
+
+ if (mode != UNIT_CHECK) {
+ if (strv_isempty(l)) {
+ c->pass_environment = strv_free(c->pass_environment);
+ unit_write_drop_in_private_format(u, mode, name, "PassEnvironment=\n");
+ } else {
+ _cleanup_free_ char *joined = NULL;
+
+ r = strv_extend_strv(&c->pass_environment, l, true);
+ if (r < 0)
+ return r;
+
+ joined = strv_join_quoted(c->pass_environment);
+ if (!joined)
+ return -ENOMEM;
- unit_write_drop_in_private_format(u, mode, name, "Environment=%s\n", joined);
+ unit_write_drop_in_private_format(u, mode, name, "PassEnvironment=%s\n", joined);
+ }
+ }
+
+ return 1;
+
+ } else if (STR_IN_SET(name, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories")) {
+
+ _cleanup_strv_free_ char **l = NULL;
+ char ***dirs;
+ char **p;
+
+ r = sd_bus_message_read_strv(message, &l);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(p, l) {
+ int offset;
+ if (!utf8_is_valid(*p))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s", name);
+
+ offset = **p == '-';
+ if (!path_is_absolute(*p + offset))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s", name);
+ }
+
+ if (mode != UNIT_CHECK) {
+ _cleanup_free_ char *joined = NULL;
+
+ if (streq(name, "ReadWriteDirectories"))
+ dirs = &c->read_write_dirs;
+ else if (streq(name, "ReadOnlyDirectories"))
+ dirs = &c->read_only_dirs;
+ else /* "InaccessibleDirectories" */
+ dirs = &c->inaccessible_dirs;
+
+ if (strv_length(l) == 0) {
+ *dirs = strv_free(*dirs);
+ unit_write_drop_in_private_format(u, mode, name, "%s=\n", name);
+ } else {
+ r = strv_extend_strv(dirs, l, true);
+
+ if (r < 0)
+ return -ENOMEM;
+
+ joined = strv_join_quoted(*dirs);
+ if (!joined)
+ return -ENOMEM;
+
+ unit_write_drop_in_private_format(u, mode, name, "%s=%s\n", name, joined);
+ }
+
+ }
+
+ return 1;
+
+ } else if (streq(name, "ProtectSystem")) {
+ const char *s;
+ ProtectSystem ps;
+
+ r = sd_bus_message_read(message, "s", &s);
+ if (r < 0)
+ return r;
+
+ r = parse_boolean(s);
+ if (r > 0)
+ ps = PROTECT_SYSTEM_YES;
+ else if (r == 0)
+ ps = PROTECT_SYSTEM_NO;
+ else {
+ ps = protect_system_from_string(s);
+ if (ps < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Failed to parse protect system value");
+ }
+
+ if (mode != UNIT_CHECK) {
+ c->protect_system = ps;
+ unit_write_drop_in_private_format(u, mode, name, "%s=%s\n", name, s);
+ }
+
+ return 1;
+
+ } else if (streq(name, "ProtectHome")) {
+ const char *s;
+ ProtectHome ph;
+
+ r = sd_bus_message_read(message, "s", &s);
+ if (r < 0)
+ return r;
+
+ r = parse_boolean(s);
+ if (r > 0)
+ ph = PROTECT_HOME_YES;
+ else if (r == 0)
+ ph = PROTECT_HOME_NO;
+ else {
+ ph = protect_home_from_string(s);
+ if (ph < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Failed to parse protect home value");
+ }
+
+ if (mode != UNIT_CHECK) {
+ c->protect_home = ph;
+ unit_write_drop_in_private_format(u, mode, name, "%s=%s\n", name, s);
+ }
+
+ return 1;
+
+ } else if (streq(name, "RuntimeDirectory")) {
+ _cleanup_strv_free_ char **l = NULL;
+ char **p;
+
+ r = sd_bus_message_read_strv(message, &l);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(p, l) {
+ if (!filename_is_valid(*p))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Runtime directory is not valid %s", *p);
+ }
+
+ if (mode != UNIT_CHECK) {
+ _cleanup_free_ char *joined = NULL;
+
+ if (strv_isempty(l)) {
+ c->runtime_directory = strv_free(c->runtime_directory);
+ unit_write_drop_in_private_format(u, mode, name, "%s=\n", name);
+ } else {
+ r = strv_extend_strv(&c->runtime_directory, l, true);
+
+ if (r < 0)
+ return -ENOMEM;
+
+ joined = strv_join_quoted(c->runtime_directory);
+ if (!joined)
+ return -ENOMEM;
+
+ unit_write_drop_in_private_format(u, mode, name, "%s=%s\n", name, joined);
+ }
}
return 1;
diff --git a/src/core/dbus-execute.h b/src/core/dbus-execute.h
index e4c2d5ddf6..c44517ea22 100644
--- a/src/core/dbus-execute.h
+++ b/src/core/dbus-execute.h
@@ -22,6 +22,7 @@
***/
#include "sd-bus.h"
+
#include "execute.h"
#define BUS_EXEC_STATUS_VTABLE(prefix, offset, flags) \
diff --git a/src/core/dbus-job.c b/src/core/dbus-job.c
index cd6b909426..90c775b638 100644
--- a/src/core/dbus-job.c
+++ b/src/core/dbus-job.c
@@ -19,12 +19,15 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "log.h"
#include "sd-bus.h"
-#include "selinux-access.h"
-#include "job.h"
+
+#include "alloc-util.h"
#include "dbus-job.h"
#include "dbus.h"
+#include "job.h"
+#include "log.h"
+#include "selinux-access.h"
+#include "string-util.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, job_type, JobType);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_state, job_state, JobState);
@@ -90,7 +93,7 @@ const sd_bus_vtable bus_job_vtable[] = {
};
static int send_new_signal(sd_bus *bus, void *userdata) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *p = NULL;
Job *j = userdata;
int r;
@@ -150,7 +153,7 @@ void bus_job_send_change_signal(Job *j) {
}
static int send_removed_signal(sd_bus *bus, void *userdata) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *p = NULL;
Job *j = userdata;
int r;
diff --git a/src/core/dbus-job.h b/src/core/dbus-job.h
index fb5f1b513e..0f2fbe2ee2 100644
--- a/src/core/dbus-job.h
+++ b/src/core/dbus-job.h
@@ -22,6 +22,7 @@
***/
#include "sd-bus.h"
+
#include "job.h"
extern const sd_bus_vtable bus_job_vtable[];
diff --git a/src/core/dbus-kill.c b/src/core/dbus-kill.c
index 3b8116281c..c633eb1b76 100644
--- a/src/core/dbus-kill.c
+++ b/src/core/dbus-kill.c
@@ -19,11 +19,10 @@
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 "kill.h"
+#include "signal-util.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_kill_mode, kill_mode, KillMode);
diff --git a/src/core/dbus-kill.h b/src/core/dbus-kill.h
index 7c15f3a90b..1d32fca547 100644
--- a/src/core/dbus-kill.h
+++ b/src/core/dbus-kill.h
@@ -22,8 +22,9 @@
***/
#include "sd-bus.h"
-#include "unit.h"
+
#include "kill.h"
+#include "unit.h"
extern const sd_bus_vtable bus_kill_vtable[];
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index 561b6f8bfa..c5c672a0a2 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -20,27 +20,33 @@
***/
#include <errno.h>
+#include <sys/prctl.h>
#include <unistd.h>
-#include "log.h"
-#include "strv.h"
+#include "alloc-util.h"
+#include "architecture.h"
#include "build.h"
-#include "install.h"
-#include "selinux-access.h"
-#include "watchdog.h"
+#include "bus-common-errors.h"
#include "clock-util.h"
-#include "path-util.h"
-#include "virt.h"
-#include "architecture.h"
-#include "env-util.h"
-#include "dbus.h"
+#include "dbus-execute.h"
#include "dbus-job.h"
#include "dbus-manager.h"
#include "dbus-unit.h"
-#include "dbus-snapshot.h"
-#include "dbus-execute.h"
-#include "bus-common-errors.h"
+#include "dbus.h"
+#include "env-util.h"
+#include "fd-util.h"
+#include "fileio.h"
#include "formats-util.h"
+#include "install.h"
+#include "log.h"
+#include "path-util.h"
+#include "selinux-access.h"
+#include "stat-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "syslog-util.h"
+#include "virt.h"
+#include "watchdog.h"
static int property_get_version(
sd_bus *bus,
@@ -81,10 +87,21 @@ static int property_get_virtualization(
void *userdata,
sd_bus_error *error) {
+ int v;
+
assert(bus);
assert(reply);
- return sd_bus_message_append(reply, "s", virtualization_to_string(detect_virtualization()));
+ v = detect_virtualization();
+
+ /* Make sure to return the empty string when we detect no virtualization, as that is the API.
+ *
+ * https://github.com/systemd/systemd/issues/1423
+ */
+
+ return sd_bus_message_append(
+ reply, "s",
+ v == VIRTUALIZATION_NONE ? "" : virtualization_to_string(v));
}
static int property_get_architecture(
@@ -111,8 +128,7 @@ static int property_get_tainted(
void *userdata,
sd_bus_error *error) {
- char buf[sizeof("split-usr:mtab-not-symlink:cgroups-missing:local-hwclock:")] = "", *e = buf;
- _cleanup_free_ char *p = NULL;
+ char buf[sizeof("split-usr:cgroups-missing:local-hwclock:")] = "", *e = buf;
Manager *m = userdata;
assert(bus);
@@ -122,9 +138,6 @@ static int property_get_tainted(
if (m->taint_usr)
e = stpcpy(e, "split-usr:");
- if (readlink_malloc("/etc/mtab", &p) < 0)
- e = stpcpy(e, "mtab-not-symlink:");
-
if (access("/proc/cgroups", F_OK) < 0)
e = stpcpy(e, "cgroups-missing:");
@@ -216,7 +229,10 @@ static int property_set_log_level(
if (r < 0)
return r;
- return log_set_max_level_from_string(t);
+ r = log_set_max_level_from_string(t);
+ if (r == 0)
+ log_info("Setting log level to %s.", t);
+ return r;
}
static int property_get_n_names(
@@ -339,6 +355,21 @@ static int property_set_runtime_watchdog(
return watchdog_set_timeout(t);
}
+static int property_get_timer_slack_nsec(
+ 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, "t", (uint64_t) prctl(PR_GET_TIMERSLACK));
+}
+
static int method_get_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *path = NULL;
Manager *m = userdata;
@@ -356,7 +387,7 @@ static int method_get_unit(sd_bus_message *message, void *userdata, sd_bus_error
return r;
if (isempty(name)) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
pid_t pid;
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
@@ -408,7 +439,7 @@ static int method_get_unit_by_pid(sd_bus_message *message, void *userdata, sd_bu
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;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
if (r < 0)
@@ -451,7 +482,7 @@ static int method_load_unit(sd_bus_message *message, void *userdata, sd_bus_erro
return r;
if (isempty(name)) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
pid_t pid;
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
@@ -602,9 +633,13 @@ static int method_set_unit_properties(sd_bus_message *message, void *userdata, s
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 is not loaded.", name);
+ r = manager_load_unit(m, name, NULL, error, &u);
+ if (r < 0)
+ return r;
+
+ r = bus_unit_check_load_state(u, error);
+ if (r < 0)
+ return r;
return bus_unit_method_set_properties(message, u, error);
}
@@ -616,6 +651,7 @@ static int transient_unit_from_message(
Unit **unit,
sd_bus_error *error) {
+ UnitType t;
Unit *u;
int r;
@@ -623,12 +659,18 @@ static int transient_unit_from_message(
assert(message);
assert(name);
+ t = unit_name_to_type(name);
+ if (t < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit name or type.");
+
+ if (!unit_vtable[t]->can_transient)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit type %s does not support transient units.", unit_type_to_string(t));
+
r = manager_load_unit(m, name, NULL, error, &u);
if (r < 0)
return r;
- if (u->load_state != UNIT_NOT_FOUND ||
- set_size(u->dependencies[UNIT_REFERENCED_BY]) > 0)
+ if (!unit_is_pristine(u))
return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS, "Unit %s already exists.", name);
/* OK, the unit failed to load and is unreferenced, now let's
@@ -642,6 +684,9 @@ static int transient_unit_from_message(
if (r < 0)
return r;
+ /* Now load the missing bits of the unit we just created */
+ manager_dispatch_load_queue(m);
+
*unit = u;
return 0;
@@ -652,8 +697,6 @@ static int transient_aux_units_from_message(
sd_bus_message *message,
sd_bus_error *error) {
- Unit *u;
- char *name = NULL;
int r;
assert(m);
@@ -664,20 +707,17 @@ static int transient_aux_units_from_message(
return r;
while ((r = sd_bus_message_enter_container(message, 'r', "sa(sv)")) > 0) {
+ const char *name = NULL;
+ Unit *u;
+
r = sd_bus_message_read(message, "s", &name);
if (r < 0)
return r;
r = transient_unit_from_message(m, message, name, &u, error);
- if (r < 0 && r != -EEXIST)
+ if (r < 0)
return r;
- if (r != -EEXIST) {
- r = unit_load(u);
- if (r < 0)
- return r;
- }
-
r = sd_bus_message_exit_container(message);
if (r < 0)
return r;
@@ -696,7 +736,6 @@ static int method_start_transient_unit(sd_bus_message *message, void *userdata,
const char *name, *smode;
Manager *m = userdata;
JobMode mode;
- UnitType t;
Unit *u;
int r;
@@ -711,13 +750,6 @@ static int method_start_transient_unit(sd_bus_message *message, void *userdata,
if (r < 0)
return r;
- t = unit_name_to_type(name);
- if (t < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit type.");
-
- if (!unit_vtable[t]->can_transient)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit type %s does not support transient units.", unit_type_to_string(t));
-
mode = job_mode_from_string(smode);
if (mode < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Job mode %s is invalid.", smode);
@@ -736,13 +768,6 @@ static int method_start_transient_unit(sd_bus_message *message, void *userdata,
if (r < 0)
return r;
- /* And load this stub fully */
- r = unit_load(u);
- if (r < 0)
- return r;
-
- manager_dispatch_load_queue(m);
-
/* Finally, start it */
return bus_unit_queue_job(message, u, JOB_START, mode, false, error);
}
@@ -843,7 +868,7 @@ static int method_reset_failed(sd_bus_message *message, void *userdata, sd_bus_e
}
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;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
Manager *m = userdata;
const char *k;
Iterator i;
@@ -931,7 +956,7 @@ static int method_list_units_filtered(sd_bus_message *message, void *userdata, s
}
static int method_list_jobs(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
Manager *m = userdata;
Iterator i;
Job *j;
@@ -1072,66 +1097,8 @@ static int method_dump(sd_bus_message *message, void *userdata, sd_bus_error *er
return sd_bus_reply_method_return(message, "s", dump);
}
-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;
- int cleanup;
- Snapshot *s = NULL;
- int r;
-
- assert(message);
- assert(m);
-
- r = mac_selinux_access_check(message, "start", error);
- if (r < 0)
- return r;
-
- r = sd_bus_message_read(message, "sb", &name, &cleanup);
- if (r < 0)
- return r;
-
- 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;
-
- path = unit_dbus_path(UNIT(s));
- if (!path)
- return -ENOMEM;
-
- return sd_bus_reply_method_return(message, "o", path);
-}
-
-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(message);
- assert(m);
-
- r = sd_bus_message_read(message, "s", &name);
- 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 does not exist.", name);
-
- 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(message, u, error);
+static int method_refuse_snapshot(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Support for snapshots has been removed.");
}
static int method_reload(sd_bus_message *message, void *userdata, sd_bus_error *error) {
@@ -1477,7 +1444,7 @@ static int method_set_exit_code(sd_bus_message *message, void *userdata, sd_bus_
}
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;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
Manager *m = userdata;
UnitFileList *item;
Hashmap *h;
@@ -1551,9 +1518,9 @@ static int method_get_unit_file_state(sd_bus_message *message, void *userdata, s
scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
- state = unit_file_get_state(scope, NULL, name);
- if (state < 0)
- return state;
+ r = unit_file_get_state(scope, NULL, name, &state);
+ if (r < 0)
+ return r;
return sd_bus_reply_method_return(message, "s", unit_file_state_to_string(state));
}
@@ -1583,7 +1550,7 @@ static int method_get_default_target(sd_bus_message *message, void *userdata, sd
}
static int send_unit_files_changed(sd_bus *bus, void *userdata) {
- _cleanup_bus_message_unref_ sd_bus_message *message = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *message = NULL;
int r;
assert(bus);
@@ -1602,7 +1569,7 @@ static int reply_unit_file_changes_and_free(
UnitFileChange *changes,
unsigned n_changes) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
unsigned i;
int r;
@@ -1640,6 +1607,7 @@ static int reply_unit_file_changes_and_free(
if (r < 0)
goto fail;
+ unit_file_changes_free(changes, n_changes);
return sd_bus_send(NULL, reply, NULL);
fail:
@@ -1681,6 +1649,8 @@ static int method_enable_unit_files_generic(
scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
r = call(scope, runtime, NULL, l, force, &changes, &n_changes);
+ if (r == -ESHUTDOWN)
+ return sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED, "Unit file is masked");
if (r < 0)
return r;
@@ -1874,8 +1844,10 @@ static int method_preset_all_unit_files(sd_bus_message *message, void *userdata,
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)
+ if (r < 0) {
+ unit_file_changes_free(changes, n_changes);
return r;
+ }
return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes);
}
@@ -1915,6 +1887,8 @@ static int method_add_dependency_unit_files(sd_bus_message *message, void *userd
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 == -ESHUTDOWN)
+ return sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED, "Unit file is masked");
if (r < 0)
return r;
@@ -1960,6 +1934,50 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_PROPERTY("ControlGroup", "s", NULL, offsetof(Manager, cgroup_root), 0),
SD_BUS_PROPERTY("SystemState", "s", property_get_system_state, 0, 0),
SD_BUS_PROPERTY("ExitCode", "y", bus_property_get_unsigned, offsetof(Manager, return_value), 0),
+ SD_BUS_PROPERTY("DefaultTimerAccuracyUSec", "t", bus_property_get_usec, offsetof(Manager, default_timer_accuracy_usec), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultTimeoutStartUSec", "t", bus_property_get_usec, offsetof(Manager, default_timeout_start_usec), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultTimeoutStopUSec", "t", bus_property_get_usec, offsetof(Manager, default_timeout_stop_usec), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultRestartUSec", "t", bus_property_get_usec, offsetof(Manager, default_restart_usec), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultStartLimitInterval", "t", bus_property_get_usec, offsetof(Manager, default_start_limit_interval), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultStartLimitBurst", "u", bus_property_get_unsigned, offsetof(Manager, default_start_limit_burst), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultCPUAccounting", "b", bus_property_get_bool, offsetof(Manager, default_cpu_accounting), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultBlockIOAccounting", "b", bus_property_get_bool, offsetof(Manager, default_blockio_accounting), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultMemoryAccounting", "b", bus_property_get_bool, offsetof(Manager, default_memory_accounting), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultTasksAccounting", "b", bus_property_get_bool, offsetof(Manager, default_tasks_accounting), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitCPU", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_CPU]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitCPUSoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_CPU]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitFSIZE", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_FSIZE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitFSIZESoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_FSIZE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitDATA", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_DATA]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitDATASoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_DATA]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitSTACK", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_STACK]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitSTACKSoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_STACK]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitCORE", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_CORE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitCORESoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_CORE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitRSS", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_RSS]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitRSSSoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_RSS]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitNOFILE", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_NOFILE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitNOFILESoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_NOFILE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitAS", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_AS]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitASSoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_AS]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitNPROC", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_NPROC]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitNPROCSoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_NPROC]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitMEMLOCK", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_MEMLOCK]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitMEMLOCKSoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_MEMLOCK]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitLOCKS", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_LOCKS]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitLOCKSSoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_LOCKS]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitSIGPENDING", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_SIGPENDING]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitSIGPENDINGSoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_SIGPENDING]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitMSGQUEUE", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_MSGQUEUE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitMSGQUEUESoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_MSGQUEUE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitNICE", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_NICE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitNICESoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_NICE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitRTPRIO", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_RTPRIO]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitRTPRIOSoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_RTPRIO]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitRTTIME", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_RTTIME]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitRTTIMESoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_RTTIME]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultTasksMax", "t", NULL, offsetof(Manager, default_tasks_max), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("TimerSlackNSec", "t", property_get_timer_slack_nsec, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_METHOD("GetUnit", "s", "o", method_get_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetUnitByPID", "u", "o", method_get_unit_by_pid, SD_BUS_VTABLE_UNPRIVILEGED),
@@ -1986,8 +2004,8 @@ const sd_bus_vtable bus_manager_vtable[] = {
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, SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD("RemoveSnapshot", "s", NULL, method_remove_snapshot, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("CreateSnapshot", "sb", "o", method_refuse_snapshot, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("RemoveSnapshot", "s", NULL, method_refuse_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),
@@ -2027,7 +2045,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
};
static int send_finished(sd_bus *bus, void *userdata) {
- _cleanup_bus_message_unref_ sd_bus_message *message = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *message = NULL;
usec_t *times = userdata;
int r;
@@ -2075,7 +2093,7 @@ void bus_manager_send_finished(
}
static int send_reloading(sd_bus *bus, void *userdata) {
- _cleanup_bus_message_unref_ sd_bus_message *message = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *message = NULL;
int r;
assert(bus);
diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c
index 24813c6d20..bc5751a10d 100644
--- a/src/core/dbus-mount.c
+++ b/src/core/dbus-mount.c
@@ -19,13 +19,14 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "unit.h"
-#include "mount.h"
+#include "bus-util.h"
+#include "dbus-cgroup.h"
#include "dbus-execute.h"
#include "dbus-kill.h"
-#include "dbus-cgroup.h"
#include "dbus-mount.h"
-#include "bus-util.h"
+#include "mount.h"
+#include "string-util.h"
+#include "unit.h"
static int property_get_what(
sd_bus *bus,
diff --git a/src/core/dbus-mount.h b/src/core/dbus-mount.h
index f7004d252f..dd0bf51bb0 100644
--- a/src/core/dbus-mount.h
+++ b/src/core/dbus-mount.h
@@ -22,6 +22,7 @@
***/
#include "sd-bus.h"
+
#include "unit.h"
extern const sd_bus_vtable bus_mount_vtable[];
diff --git a/src/core/dbus-path.c b/src/core/dbus-path.c
index 683561999b..e0544e9161 100644
--- a/src/core/dbus-path.c
+++ b/src/core/dbus-path.c
@@ -19,10 +19,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "unit.h"
-#include "path.h"
-#include "dbus-path.h"
#include "bus-util.h"
+#include "dbus-path.h"
+#include "path.h"
+#include "string-util.h"
+#include "unit.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, path_result, PathResult);
diff --git a/src/core/dbus-scope.c b/src/core/dbus-scope.c
index f8fb373bf0..920362c61f 100644
--- a/src/core/dbus-scope.c
+++ b/src/core/dbus-scope.c
@@ -19,17 +19,18 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "selinux-access.h"
-#include "unit.h"
-#include "scope.h"
-#include "dbus.h"
-#include "bus-util.h"
-#include "bus-internal.h"
+#include "alloc-util.h"
#include "bus-common-errors.h"
-#include "dbus-unit.h"
+#include "bus-internal.h"
+#include "bus-util.h"
#include "dbus-cgroup.h"
#include "dbus-kill.h"
#include "dbus-scope.h"
+#include "dbus-unit.h"
+#include "dbus.h"
+#include "scope.h"
+#include "selinux-access.h"
+#include "unit.h"
static int bus_scope_abandon(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Scope *s = userdata;
@@ -204,7 +205,7 @@ int bus_scope_commit_properties(Unit *u) {
}
int bus_scope_send_request_stop(Scope *s) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *p = NULL;
int r;
diff --git a/src/core/dbus-scope.h b/src/core/dbus-scope.h
index 33beda47b7..4fb0b25e09 100644
--- a/src/core/dbus-scope.h
+++ b/src/core/dbus-scope.h
@@ -22,6 +22,7 @@
***/
#include "sd-bus.h"
+
#include "unit.h"
extern const sd_bus_vtable bus_scope_vtable[];
diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c
index 3436342bef..24f611a593 100644
--- a/src/core/dbus-service.c
+++ b/src/core/dbus-service.c
@@ -19,15 +19,20 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "strv.h"
-#include "path-util.h"
-#include "unit.h"
-#include "service.h"
+#include "alloc-util.h"
+#include "async.h"
+#include "bus-util.h"
+#include "dbus-cgroup.h"
#include "dbus-execute.h"
#include "dbus-kill.h"
-#include "dbus-cgroup.h"
#include "dbus-service.h"
-#include "bus-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "path-util.h"
+#include "service.h"
+#include "string-util.h"
+#include "strv.h"
+#include "unit.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, service_type, ServiceType);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, service_result, ServiceResult);
@@ -58,7 +63,8 @@ const sd_bus_vtable bus_service_vtable[] = {
SD_BUS_PROPERTY("MainPID", "u", bus_property_get_pid, offsetof(Service, main_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("ControlPID", "u", bus_property_get_pid, offsetof(Service, control_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("BusName", "s", NULL, offsetof(Service, bus_name), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("FileDescriptorStoreMax", "u", NULL, offsetof(Service, n_fd_store_max), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("FileDescriptorStoreMax", "u", bus_property_get_unsigned, offsetof(Service, n_fd_store_max), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("NFileDescriptorStore", "u", bus_property_get_unsigned, offsetof(Service, n_fd_store), 0),
SD_BUS_PROPERTY("StatusText", "s", NULL, offsetof(Service, status_text), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("StatusErrno", "i", NULL, offsetof(Service, status_errno), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Service, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
@@ -120,6 +126,37 @@ static int bus_service_set_transient_property(
return 1;
+ } else if (STR_IN_SET(name,
+ "StandardInputFileDescriptor",
+ "StandardOutputFileDescriptor",
+ "StandardErrorFileDescriptor")) {
+ int fd;
+
+ r = sd_bus_message_read(message, "h", &fd);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ int copy;
+
+ copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+ if (copy < 0)
+ return -errno;
+
+ if (streq(name, "StandardInputFileDescriptor")) {
+ asynchronous_close(s->stdin_fd);
+ s->stdin_fd = copy;
+ } else if (streq(name, "StandardOutputFileDescriptor")) {
+ asynchronous_close(s->stdout_fd);
+ s->stdout_fd = copy;
+ } else {
+ asynchronous_close(s->stderr_fd);
+ s->stderr_fd = copy;
+ }
+ }
+
+ return 1;
+
} else if (streq(name, "ExecStart")) {
unsigned n = 0;
@@ -211,7 +248,9 @@ static int bus_service_set_transient_property(
a);
}
- fflush(f);
+ r = fflush_and_check(f);
+ if (r < 0)
+ return r;
unit_write_drop_in_private(UNIT(s), mode, name, buf);
}
diff --git a/src/core/dbus-service.h b/src/core/dbus-service.h
index aab9f7aa26..a67b64ab5b 100644
--- a/src/core/dbus-service.h
+++ b/src/core/dbus-service.h
@@ -22,6 +22,7 @@
***/
#include "sd-bus.h"
+
#include "unit.h"
extern const sd_bus_vtable bus_service_vtable[];
diff --git a/src/core/dbus-slice.c b/src/core/dbus-slice.c
index 09e78d1f33..469e3e1c93 100644
--- a/src/core/dbus-slice.c
+++ b/src/core/dbus-slice.c
@@ -19,10 +19,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "unit.h"
-#include "slice.h"
#include "dbus-cgroup.h"
#include "dbus-slice.h"
+#include "slice.h"
+#include "unit.h"
const sd_bus_vtable bus_slice_vtable[] = {
SD_BUS_VTABLE_START(0),
diff --git a/src/core/dbus-slice.h b/src/core/dbus-slice.h
index eadc3b1a9c..117d11471b 100644
--- a/src/core/dbus-slice.h
+++ b/src/core/dbus-slice.h
@@ -22,6 +22,7 @@
***/
#include "sd-bus.h"
+
#include "unit.h"
extern const sd_bus_vtable bus_slice_vtable[];
diff --git a/src/core/dbus-snapshot.c b/src/core/dbus-snapshot.c
deleted file mode 100644
index cfe44c9c15..0000000000
--- a/src/core/dbus-snapshot.c
+++ /dev/null
@@ -1,55 +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 "selinux-access.h"
-#include "unit.h"
-#include "dbus.h"
-#include "snapshot.h"
-#include "dbus-snapshot.h"
-
-int bus_snapshot_method_remove(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- Snapshot *s = userdata;
- int r;
-
- assert(message);
- assert(s);
-
- 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 */
-
- snapshot_remove(s);
-
- return sd_bus_reply_method_return(message, NULL);
-}
-
-const sd_bus_vtable bus_snapshot_vtable[] = {
- SD_BUS_VTABLE_START(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-socket.c b/src/core/dbus-socket.c
index 86732e2a45..895dd07753 100644
--- a/src/core/dbus-socket.c
+++ b/src/core/dbus-socket.c
@@ -19,12 +19,14 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "unit.h"
-#include "socket.h"
-#include "dbus-execute.h"
+#include "alloc-util.h"
+#include "bus-util.h"
#include "dbus-cgroup.h"
+#include "dbus-execute.h"
#include "dbus-socket.h"
-#include "bus-util.h"
+#include "socket.h"
+#include "string-util.h"
+#include "unit.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, socket_result, SocketResult);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_bind_ipv6_only, socket_address_bind_ipv6_only, SocketAddressBindIPv6Only);
@@ -84,6 +86,25 @@ static int property_get_listen(
return sd_bus_message_close_container(reply);
}
+
+static int property_get_fdname(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Socket *s = SOCKET(userdata);
+
+ assert(bus);
+ assert(reply);
+ assert(s);
+
+ return sd_bus_message_append(reply, "s", socket_fdname(s));
+}
+
const sd_bus_vtable bus_socket_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("BindIPv6Only", "s", property_get_bind_ipv6_only, offsetof(Socket, bind_ipv6_only), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -95,6 +116,7 @@ const sd_bus_vtable bus_socket_vtable[] = {
SD_BUS_PROPERTY("SocketMode", "u", bus_property_get_mode, offsetof(Socket, socket_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DirectoryMode", "u", bus_property_get_mode, offsetof(Socket, directory_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Accept", "b", bus_property_get_bool, offsetof(Socket, accept), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("Writable", "b", bus_property_get_bool, offsetof(Socket, writable), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("KeepAlive", "b", bus_property_get_bool, offsetof(Socket, keep_alive), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("KeepAliveTimeUSec", "t", bus_property_get_usec, offsetof(Socket, keep_alive_time), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("KeepAliveIntervalUSec", "t", bus_property_get_usec, offsetof(Socket, keep_alive_interval), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -127,6 +149,8 @@ const sd_bus_vtable bus_socket_vtable[] = {
SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Socket, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("NConnections", "u", bus_property_get_unsigned, offsetof(Socket, n_connections), 0),
SD_BUS_PROPERTY("NAccepted", "u", bus_property_get_unsigned, offsetof(Socket, n_accepted), 0),
+ SD_BUS_PROPERTY("FileDescriptorName", "s", property_get_fdname, 0, 0),
+ SD_BUS_PROPERTY("SocketProtocol", "i", bus_property_get_int, offsetof(Socket, socket_protocol), SD_BUS_VTABLE_PROPERTY_CONST),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPre", offsetof(Socket, exec_command[SOCKET_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPost", offsetof(Socket, exec_command[SOCKET_EXEC_START_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStopPre", offsetof(Socket, exec_command[SOCKET_EXEC_STOP_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
diff --git a/src/core/dbus-socket.h b/src/core/dbus-socket.h
index 17164d9871..8dad6ea2e9 100644
--- a/src/core/dbus-socket.h
+++ b/src/core/dbus-socket.h
@@ -22,6 +22,7 @@
***/
#include "sd-bus.h"
+
#include "unit.h"
extern const sd_bus_vtable bus_socket_vtable[];
diff --git a/src/core/dbus-swap.c b/src/core/dbus-swap.c
index 0093371306..f2a0f1d172 100644
--- a/src/core/dbus-swap.c
+++ b/src/core/dbus-swap.c
@@ -20,12 +20,13 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "unit.h"
-#include "swap.h"
-#include "dbus-execute.h"
+#include "bus-util.h"
#include "dbus-cgroup.h"
+#include "dbus-execute.h"
#include "dbus-swap.h"
-#include "bus-util.h"
+#include "string-util.h"
+#include "swap.h"
+#include "unit.h"
static int property_get_priority(
sd_bus *bus,
diff --git a/src/core/dbus-swap.h b/src/core/dbus-swap.h
index 9469f68ab8..a414ca7f75 100644
--- a/src/core/dbus-swap.h
+++ b/src/core/dbus-swap.h
@@ -23,6 +23,7 @@
***/
#include "sd-bus.h"
+
#include "unit.h"
extern const sd_bus_vtable bus_swap_vtable[];
diff --git a/src/core/dbus-target.c b/src/core/dbus-target.c
index 350f5c3ed2..654bcf1a29 100644
--- a/src/core/dbus-target.c
+++ b/src/core/dbus-target.c
@@ -19,8 +19,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "unit.h"
#include "dbus-target.h"
+#include "unit.h"
const sd_bus_vtable bus_target_vtable[] = {
SD_BUS_VTABLE_START(0),
diff --git a/src/core/dbus-target.h b/src/core/dbus-target.h
index 4c4297bc9e..6be9c9f708 100644
--- a/src/core/dbus-target.h
+++ b/src/core/dbus-target.h
@@ -21,5 +21,6 @@
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 8ea2cf84a4..ec301df6d7 100644
--- a/src/core/dbus-timer.c
+++ b/src/core/dbus-timer.c
@@ -19,11 +19,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "unit.h"
-#include "timer.h"
-#include "dbus-timer.h"
+#include "alloc-util.h"
#include "bus-util.h"
+#include "dbus-timer.h"
#include "strv.h"
+#include "timer.h"
+#include "unit.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, timer_result, TimerResult);
@@ -179,8 +180,10 @@ const sd_bus_vtable bus_timer_vtable[] = {
BUS_PROPERTY_DUAL_TIMESTAMP("LastTriggerUSec", offsetof(Timer, last_trigger), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Timer, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("AccuracyUSec", "t", bus_property_get_usec, offsetof(Timer, accuracy_usec), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("RandomizedDelayUSec", "t", bus_property_get_usec, offsetof(Timer, random_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Persistent", "b", bus_property_get_bool, offsetof(Timer, persistent), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("WakeSystem", "b", bus_property_get_bool, offsetof(Timer, wake_system), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("RemainAfterElapse", "b", bus_property_get_bool, offsetof(Timer, remain_after_elapse), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_VTABLE_END
};
@@ -281,8 +284,23 @@ static int bus_timer_set_transient_property(
return 1;
- } else if (streq(name, "WakeSystem")) {
+ } else if (streq(name, "RandomizedDelayUSec")) {
+ usec_t u = 0;
+
+ r = sd_bus_message_read(message, "t", &u);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ char time[FORMAT_TIMESPAN_MAX];
+ t->random_usec = u;
+ unit_write_drop_in_private_format(UNIT(t), mode, name, "RandomizedDelaySec=%s\n", format_timespan(time, sizeof(time), u, USEC_PER_MSEC));
+ }
+
+ return 1;
+
+ } else if (streq(name, "WakeSystem")) {
int b;
r = sd_bus_message_read(message, "b", &b);
@@ -291,11 +309,24 @@ static int bus_timer_set_transient_property(
if (mode != UNIT_CHECK) {
t->wake_system = b;
- unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s\n", name, yes_no(t->wake_system));
+ unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s\n", name, yes_no(b));
}
return 1;
+ } else if (streq(name, "RemainAfterElapse")) {
+ int b;
+
+ r = sd_bus_message_read(message, "b", &b);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ t->remain_after_elapse = b;
+ unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s\n", name, yes_no(b));
+ }
+
+ return 1;
}
return 0;
diff --git a/src/core/dbus-timer.h b/src/core/dbus-timer.h
index 103172f055..ca35c4b8c1 100644
--- a/src/core/dbus-timer.h
+++ b/src/core/dbus-timer.h
@@ -22,6 +22,7 @@
***/
#include "sd-bus.h"
+
#include "unit.h"
extern const sd_bus_vtable bus_timer_vtable[];
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index cd88a87340..e4d2c08972 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -20,14 +20,19 @@
***/
#include "sd-bus.h"
+
+#include "alloc-util.h"
+#include "bus-common-errors.h"
+#include "cgroup-util.h"
+#include "dbus-unit.h"
+#include "dbus.h"
+#include "locale-util.h"
#include "log.h"
#include "selinux-access.h"
-#include "cgroup-util.h"
-#include "strv.h"
-#include "bus-common-errors.h"
#include "special.h"
-#include "dbus.h"
-#include "dbus-unit.h"
+#include "string-util.h"
+#include "strv.h"
+#include "user-util.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_load_state, unit_load_state, UnitLoadState);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_job_mode, job_mode, JobMode);
@@ -113,6 +118,22 @@ static int property_get_dependencies(
return sd_bus_message_close_container(reply);
}
+static int property_get_obsolete_dependencies(
+ 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);
+
+ /* For dependency types we don't support anymore always return an empty array */
+ return sd_bus_message_append(reply, "as", 0);
+}
+
static int property_get_description(
sd_bus *bus,
const char *path,
@@ -378,7 +399,7 @@ static int property_get_load_error(
void *userdata,
sd_bus_error *error) {
- _cleanup_bus_error_free_ sd_bus_error e = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL;
Unit *u = userdata;
assert(bus);
@@ -616,16 +637,12 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_PROPERTY("Names", "as", property_get_names, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Following", "s", property_get_following, 0, 0),
SD_BUS_PROPERTY("Requires", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRES]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("RequiresOverridable", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Requisite", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("RequisiteOverridable", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Wants", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_WANTS]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("BindsTo", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_BINDS_TO]), SD_BUS_VTABLE_PROPERTY_CONST),
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),
@@ -639,6 +656,10 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_PROPERTY("PropagatesReloadTo", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ReloadPropagatedFrom", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("JoinsNamespaceOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_JOINS_NAMESPACE_OF]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("RequiresOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
+ SD_BUS_PROPERTY("RequisiteOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
+ SD_BUS_PROPERTY("RequiredByOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
+ SD_BUS_PROPERTY("RequisiteOfOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
SD_BUS_PROPERTY("RequiresMountsFor", "as", NULL, offsetof(Unit, requires_mounts_for), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Documentation", "as", NULL, offsetof(Unit, documentation), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Description", "s", property_get_description, 0, SD_BUS_VTABLE_PROPERTY_CONST),
@@ -666,7 +687,6 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_PROPERTY("DefaultDependencies", "b", bus_property_get_bool, offsetof(Unit, default_dependencies), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("OnFailureJobMode", "s", property_get_job_mode, offsetof(Unit, on_failure_job_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("IgnoreOnIsolate", "b", bus_property_get_bool, offsetof(Unit, ignore_on_isolate), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("IgnoreOnSnapshot", "b", bus_property_get_bool, offsetof(Unit, ignore_on_snapshot), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("NeedDaemonReload", "b", property_get_need_daemon_reload, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("JobTimeoutUSec", "t", bus_property_get_usec, offsetof(Unit, job_timeout), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("JobTimeoutAction", "s", property_get_failure_action, offsetof(Unit, job_timeout_action), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -679,7 +699,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_PROPERTY("Asserts", "a(sbbsi)", property_get_conditions, offsetof(Unit, asserts), 0),
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_PROPERTY("NetClass", "u", bus_property_get_unsigned, offsetof(Unit, cgroup_netclass_id), 0),
+ SD_BUS_PROPERTY("NetClass", "u", NULL, offsetof(Unit, cgroup_netclass_id), 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),
@@ -826,7 +846,7 @@ const sd_bus_vtable bus_unit_cgroup_vtable[] = {
};
static int send_new_signal(sd_bus *bus, void *userdata) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *p = NULL;
Unit *u = userdata;
int r;
@@ -903,7 +923,7 @@ void bus_unit_send_change_signal(Unit *u) {
}
static int send_removed_signal(sd_bus *bus, void *userdata) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *p = NULL;
Unit *u = userdata;
int r;
@@ -984,10 +1004,11 @@ int bus_unit_queue_job(
if ((type == JOB_START && u->refuse_manual_start) ||
(type == JOB_STOP && u->refuse_manual_stop) ||
- ((type == JOB_RESTART || type == JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop)))
+ ((type == JOB_RESTART || type == JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop)) ||
+ (type == JOB_RELOAD_OR_START && job_type_collapse(type, u) == JOB_START && u->refuse_manual_start))
return sd_bus_error_setf(error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, unit %s may be requested by dependency only.", u->id);
- r = manager_add_job(u->manager, type, u, mode, true, error, &j);
+ r = manager_add_job(u->manager, type, u, mode, error, &j);
if (r < 0)
return r;
@@ -1103,9 +1124,15 @@ static int bus_unit_set_transient_property(
UnitDependency d;
const char *other;
- d = unit_dependency_from_string(name);
- if (d < 0)
- return -EINVAL;
+ if (streq(name, "RequiresOverridable"))
+ d = UNIT_REQUIRES; /* redirect for obsolete unit dependency type */
+ else if (streq(name, "RequisiteOverridable"))
+ d = UNIT_REQUISITE; /* same here */
+ else {
+ d = unit_dependency_from_string(name);
+ if (d < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit dependency: %s", name);
+ }
r = sd_bus_message_enter_container(message, 'a', "s");
if (r < 0)
@@ -1224,3 +1251,20 @@ int bus_unit_set_properties(
return n;
}
+
+int bus_unit_check_load_state(Unit *u, sd_bus_error *error) {
+
+ if (u->load_state == UNIT_LOADED)
+ return 0;
+
+ /* Give a better description of the unit error when
+ * possible. Note that in the case of UNIT_MASKED, load_error
+ * is not set. */
+ if (u->load_state == UNIT_MASKED)
+ return sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED, "Unit is masked.");
+
+ if (u->load_state == UNIT_NOT_FOUND)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit not found.");
+
+ return sd_bus_error_set_errnof(error, u->load_error, "Unit is not loaded properly: %m.");
+}
diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h
index b622e0ae8d..ac9ee2d6b8 100644
--- a/src/core/dbus-unit.h
+++ b/src/core/dbus-unit.h
@@ -22,6 +22,7 @@
***/
#include "sd-bus.h"
+
#include "unit.h"
extern const sd_bus_vtable bus_unit_vtable[];
@@ -37,3 +38,5 @@ int bus_unit_method_reset_failed(sd_bus_message *message, void *userdata, sd_bus
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_message *message, void *userdata, sd_bus_error *error);
+
+int bus_unit_check_load_state(Unit *u, sd_bus_error *error);
diff --git a/src/core/dbus.c b/src/core/dbus.c
index 2d6a1ff836..1d89b9e250 100644
--- a/src/core/dbus.c
+++ b/src/core/dbus.c
@@ -19,29 +19,34 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/epoll.h>
#include <errno.h>
+#include <sys/epoll.h>
#include <unistd.h>
#include "sd-bus.h"
-#include "log.h"
-#include "strv.h"
-#include "mkdir.h"
-#include "missing.h"
-#include "dbus-unit.h"
-#include "dbus-job.h"
-#include "dbus-manager.h"
+
+#include "alloc-util.h"
+#include "bus-common-errors.h"
+#include "bus-error.h"
+#include "bus-internal.h"
+#include "bus-util.h"
+#include "dbus-cgroup.h"
#include "dbus-execute.h"
+#include "dbus-job.h"
#include "dbus-kill.h"
-#include "dbus-cgroup.h"
-#include "special.h"
+#include "dbus-manager.h"
+#include "dbus-unit.h"
#include "dbus.h"
-#include "bus-util.h"
-#include "bus-error.h"
-#include "bus-common-errors.h"
-#include "strxcpyx.h"
-#include "bus-internal.h"
+#include "fd-util.h"
+#include "log.h"
+#include "missing.h"
+#include "mkdir.h"
#include "selinux-access.h"
+#include "special.h"
+#include "string-util.h"
+#include "strv.h"
+#include "strxcpyx.h"
+#include "user-util.h"
#define CONNECTIONS_MAX 4096
@@ -69,7 +74,7 @@ int bus_send_queued_message(Manager *m) {
}
static int signal_agent_released(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
const char *cgroup, *me;
Manager *m = userdata;
uid_t sender_uid;
@@ -141,8 +146,8 @@ static int signal_disconnected(sd_bus_message *message, void *userdata, sd_bus_e
}
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;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
Manager *m = userdata;
const char *name;
Unit *u;
@@ -172,7 +177,7 @@ static int signal_activation_request(sd_bus_message *message, void *userdata, sd
goto failed;
}
- r = manager_add_job(m, JOB_START, u, JOB_REPLACE, true, &error, NULL);
+ r = manager_add_job(m, JOB_START, u, JOB_REPLACE, &error, NULL);
if (r < 0)
goto failed;
@@ -240,7 +245,7 @@ static int mac_selinux_filter(sd_bus_message *message, void *userdata, sd_bus_er
}
if (streq_ptr(path, "/org/freedesktop/systemd1/unit/self")) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
pid_t pid;
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
@@ -299,7 +304,7 @@ static int find_unit(Manager *m, sd_bus *bus, const char *path, Unit **unit, sd_
assert(path);
if (streq_ptr(path, "/org/freedesktop/systemd1/unit/self")) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
sd_bus_message *message;
pid_t pid;
@@ -612,7 +617,7 @@ static int bus_setup_disconnected_match(Manager *m, sd_bus *bus) {
}
static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- _cleanup_bus_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
_cleanup_close_ int nfd = -1;
Manager *m = userdata;
sd_id128_t id;
@@ -729,9 +734,11 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void
return 0;
}
-static int bus_list_names(Manager *m, sd_bus *bus) {
+int manager_sync_bus_names(Manager *m, sd_bus *bus) {
_cleanup_strv_free_ char **names = NULL;
- char **i;
+ const char *name;
+ Iterator i;
+ Unit *u;
int r;
assert(m);
@@ -741,15 +748,55 @@ static int bus_list_names(Manager *m, sd_bus *bus) {
if (r < 0)
return log_error_errno(r, "Failed to get initial list of names: %m");
- /* This is a bit hacky, we say the owner of the name is the
- * name itself, because we don't want the extra traffic to
- * figure out the real owner. */
- STRV_FOREACH(i, names) {
- Unit *u;
+ /* We have to synchronize the current bus names with the
+ * list of active services. To do this, walk the list of
+ * all units with bus names. */
+ HASHMAP_FOREACH_KEY(u, name, m->watch_bus, i) {
+ Service *s = SERVICE(u);
+
+ assert(s);
- u = hashmap_get(m->watch_bus, *i);
- if (u)
- UNIT_VTABLE(u)->bus_name_owner_change(u, *i, NULL, *i);
+ if (!streq_ptr(s->bus_name, name)) {
+ log_unit_warning(u, "Bus name has changed from %s → %s, ignoring.", s->bus_name, name);
+ continue;
+ }
+
+ /* Check if a service's bus name is in the list of currently
+ * active names */
+ if (strv_contains(names, name)) {
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+ const char *unique;
+
+ /* If it is, determine its current owner */
+ r = sd_bus_get_name_creds(bus, name, SD_BUS_CREDS_UNIQUE_NAME, &creds);
+ if (r < 0) {
+ log_error_errno(r, "Failed to get bus name owner %s: %m", name);
+ continue;
+ }
+
+ r = sd_bus_creds_get_unique_name(creds, &unique);
+ if (r < 0) {
+ log_error_errno(r, "Failed to get unique name for %s: %m", name);
+ continue;
+ }
+
+ /* Now, let's compare that to the previous bus owner, and
+ * if it's still the same, all is fine, so just don't
+ * bother the service. Otherwise, the name has apparently
+ * changed, so synthesize a name owner changed signal. */
+
+ if (!streq_ptr(unique, s->bus_name_owner))
+ UNIT_VTABLE(u)->bus_name_owner_change(u, name, s->bus_name_owner, unique);
+ } else {
+ /* So, the name we're watching is not on the bus.
+ * This either means it simply hasn't appeared yet,
+ * or it was lost during the daemon reload.
+ * Check if the service has a stored name owner,
+ * and synthesize a name loss signal in this case. */
+
+ if (s->bus_name_owner)
+ UNIT_VTABLE(u)->bus_name_owner_change(u, name, s->bus_name_owner, NULL);
+ }
}
return 0;
@@ -777,9 +824,9 @@ static int bus_setup_api(Manager *m, sd_bus *bus) {
return r;
HASHMAP_FOREACH_KEY(u, name, m->watch_bus, i) {
- r = unit_install_bus_match(bus, u, name);
+ r = unit_install_bus_match(u, bus, name);
if (r < 0)
- log_error_errno(r, "Failed to subscribe to NameOwnerChanged signal: %m");
+ log_error_errno(r, "Failed to subscribe to NameOwnerChanged signal for '%s': %m", name);
}
r = sd_bus_add_match(
@@ -803,14 +850,16 @@ static int bus_setup_api(Manager *m, sd_bus *bus) {
if (r < 0)
return log_error_errno(r, "Failed to register name: %m");
- bus_list_names(m, bus);
+ r = manager_sync_bus_names(m, bus);
+ if (r < 0)
+ return r;
log_debug("Successfully connected to API bus.");
return 0;
}
static int bus_init_api(Manager *m) {
- _cleanup_bus_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
int r;
if (m->api_bus)
@@ -878,7 +927,7 @@ static int bus_setup_system(Manager *m, sd_bus *bus) {
}
static int bus_init_system(Manager *m) {
- _cleanup_bus_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
int r;
if (m->system_bus)
diff --git a/src/core/dbus.h b/src/core/dbus.h
index 4f06ad11c4..ff761668f3 100644
--- a/src/core/dbus.h
+++ b/src/core/dbus.h
@@ -34,6 +34,8 @@ void bus_track_serialize(sd_bus_track *t, FILE *f);
int bus_track_deserialize_item(char ***l, const char *line);
int bus_track_coldplug(Manager *m, sd_bus_track **t, char ***l);
+int manager_sync_bus_names(Manager *m, sd_bus *bus);
+
int bus_foreach_bus(Manager *m, sd_bus_track *subscribed2, int (*send_message)(sd_bus *bus, void *userdata), void *userdata);
int bus_verify_manage_units_async(Manager *m, sd_bus_message *call, sd_bus_error *error);
diff --git a/src/core/device.c b/src/core/device.c
index 0b54518691..56ed947089 100644
--- a/src/core/device.c
+++ b/src/core/device.c
@@ -21,16 +21,21 @@
#include <errno.h>
#include <sys/epoll.h>
-#include <libudev.h>
-#include "log.h"
-#include "unit-name.h"
+#include "libudev.h"
+
+#include "alloc-util.h"
#include "dbus-device.h"
+#include "device.h"
+#include "log.h"
+#include "parse-util.h"
#include "path-util.h"
+#include "stat-util.h"
+#include "string-util.h"
+#include "swap.h"
#include "udev-util.h"
+#include "unit-name.h"
#include "unit.h"
-#include "swap.h"
-#include "device.h"
static const UnitActiveState state_translation_table[_DEVICE_STATE_MAX] = {
[DEVICE_DEAD] = UNIT_INACTIVE,
@@ -112,7 +117,6 @@ static void device_init(Unit *u) {
u->job_timeout = u->manager->default_timeout_start_usec;
u->ignore_on_isolate = true;
- u->ignore_on_snapshot = true;
}
static void device_done(Unit *u) {
@@ -263,7 +267,7 @@ static int device_add_udev_wants(Unit *u, struct udev_device *dev) {
assert(u);
assert(dev);
- property = u->manager->running_as == MANAGER_USER ? "MANAGER_USER_WANTS" : "SYSTEMD_WANTS";
+ property = u->manager->running_as == MANAGER_USER ? "SYSTEMD_USER_WANTS" : "SYSTEMD_WANTS";
wants = udev_device_get_property_value(dev, property);
if (!wants)
return 0;
@@ -597,7 +601,7 @@ static void device_shutdown(Manager *m) {
m->devices_by_sysfs = hashmap_free(m->devices_by_sysfs);
}
-static int device_enumerate(Manager *m) {
+static void device_enumerate(Manager *m) {
_cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
struct udev_list_entry *item = NULL, *first = NULL;
int r;
@@ -607,7 +611,7 @@ static int device_enumerate(Manager *m) {
if (!m->udev_monitor) {
m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev");
if (!m->udev_monitor) {
- r = -ENOMEM;
+ log_oom();
goto fail;
}
@@ -617,37 +621,49 @@ static int device_enumerate(Manager *m) {
(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)
+ if (r < 0) {
+ log_error_errno(r, "Failed to add udev tag match: %m");
goto fail;
+ }
r = udev_monitor_enable_receiving(m->udev_monitor);
- if (r < 0)
+ if (r < 0) {
+ log_error_errno(r, "Failed to enable udev event reception: %m");
goto fail;
+ }
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)
+ if (r < 0) {
+ log_error_errno(r, "Failed to watch udev file descriptor: %m");
goto fail;
+ }
(void) sd_event_source_set_description(m->udev_event_source, "device");
}
e = udev_enumerate_new(m->udev);
if (!e) {
- r = -ENOMEM;
+ log_oom();
goto fail;
}
r = udev_enumerate_add_match_tag(e, "systemd");
- if (r < 0)
+ if (r < 0) {
+ log_error_errno(r, "Failed to create udev tag enumeration: %m");
goto fail;
+ }
r = udev_enumerate_add_match_is_initialized(e);
- if (r < 0)
+ if (r < 0) {
+ log_error_errno(r, "Failed to install initialization match into enumeration: %m");
goto fail;
+ }
r = udev_enumerate_scan_devices(e);
- if (r < 0)
+ if (r < 0) {
+ log_error_errno(r, "Failed to enumerate devices: %m");
goto fail;
+ }
first = udev_enumerate_get_list_entry(e);
udev_list_entry_foreach(item, first) {
@@ -670,13 +686,10 @@ static int device_enumerate(Manager *m) {
device_update_found_by_sysfs(m, sysfs, true, DEVICE_FOUND_UDEV, false);
}
- return 0;
+ return;
fail:
- log_error_errno(r, "Failed to enumerate devices: %m");
-
device_shutdown(m);
- return r;
}
static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
@@ -816,14 +829,6 @@ int device_found_node(Manager *m, const char *node, bool add, DeviceFound found,
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_TENTATIVE] = "tentative",
- [DEVICE_PLUGGED] = "plugged",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(device_state, DeviceState);
-
const UnitVTable device_vtable = {
.object_size = sizeof(Device),
.sections =
diff --git a/src/core/device.h b/src/core/device.h
index 10ab113176..da8737870b 100644
--- a/src/core/device.h
+++ b/src/core/device.h
@@ -23,16 +23,6 @@
typedef struct Device Device;
-/* We simply watch devices, we cannot plug/unplug them. That
- * simplifies the state engine greatly */
-typedef enum DeviceState {
- DEVICE_DEAD,
- 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,
@@ -56,7 +46,4 @@ struct Device {
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 7796c07fcf..d70ba2be17 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -21,18 +21,18 @@
#include <errno.h>
#include <fcntl.h>
-#include <unistd.h>
-#include <string.h>
+#include <glob.h>
+#include <grp.h>
+#include <poll.h>
#include <signal.h>
-#include <sys/socket.h>
-#include <sys/un.h>
+#include <string.h>
+#include <sys/personality.h>
#include <sys/prctl.h>
+#include <sys/socket.h>
#include <sys/stat.h>
-#include <grp.h>
-#include <poll.h>
-#include <glob.h>
+#include <sys/un.h>
+#include <unistd.h>
#include <utmpx.h>
-#include <sys/personality.h>
#ifdef HAVE_PAM
#include <security/pam_appl.h>
@@ -50,47 +50,56 @@
#include <sys/apparmor.h>
#endif
-#include "barrier.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 "ioprio.h"
-#include "securebits.h"
-#include "namespace.h"
-#include "exit-status.h"
-#include "missing.h"
-#include "utmp-wtmp.h"
-#include "def.h"
-#include "path-util.h"
-#include "env-util.h"
-#include "fileio.h"
-#include "unit.h"
-#include "async.h"
-#include "selinux-util.h"
-#include "errno-list.h"
+
#include "af-list.h"
-#include "mkdir.h"
-#include "smack-util.h"
+#include "alloc-util.h"
+#ifdef HAVE_APPARMOR
+#include "apparmor-util.h"
+#endif
+#include "async.h"
+#include "barrier.h"
#include "bus-endpoint.h"
#include "cap-list.h"
+#include "capability-util.h"
+#include "def.h"
+#include "env-util.h"
+#include "errno-list.h"
+#include "execute.h"
+#include "exit-status.h"
+#include "fd-util.h"
+#include "fileio.h"
#include "formats-util.h"
+#include "fs-util.h"
+#include "glob-util.h"
+#include "io-util.h"
+#include "ioprio.h"
+#include "log.h"
+#include "macro.h"
+#include "missing.h"
+#include "mkdir.h"
+#include "namespace.h"
+#include "parse-util.h"
+#include "path-util.h"
#include "process-util.h"
-#include "terminal-util.h"
-#include "signal-util.h"
-
-#ifdef HAVE_APPARMOR
-#include "apparmor-util.h"
-#endif
-
+#include "rlimit-util.h"
+#include "rm-rf.h"
#ifdef HAVE_SECCOMP
#include "seccomp-util.h"
#endif
-
-#include "execute.h"
+#include "securebits.h"
+#include "selinux-util.h"
+#include "signal-util.h"
+#include "smack-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "strv.h"
+#include "syslog-util.h"
+#include "terminal-util.h"
+#include "unit.h"
+#include "user-util.h"
+#include "util.h"
+#include "utmp-wtmp.h"
#define IDLE_TIMEOUT_USEC (5*USEC_PER_SEC)
#define IDLE_TIMEOUT2_USEC (1*USEC_PER_SEC)
@@ -358,12 +367,28 @@ static int fixup_output(ExecOutput std_output, int socket_fd) {
return std_output;
}
-static int setup_input(const ExecContext *context, int socket_fd, bool apply_tty_stdin) {
+static int setup_input(
+ const ExecContext *context,
+ const ExecParameters *params,
+ int socket_fd) {
+
ExecInput i;
assert(context);
+ assert(params);
+
+ if (params->stdin_fd >= 0) {
+ if (dup2(params->stdin_fd, STDIN_FILENO) < 0)
+ return -errno;
+
+ /* Try to make this the controlling tty, if it is a tty, and reset it */
+ (void) ioctl(STDIN_FILENO, TIOCSCTTY, context->std_input == EXEC_INPUT_TTY_FORCE);
+ (void) reset_terminal_fd(STDIN_FILENO, true);
+
+ return STDIN_FILENO;
+ }
- i = fixup_input(context->std_input, socket_fd, apply_tty_stdin);
+ i = fixup_input(context->std_input, socket_fd, params->apply_tty_stdin);
switch (i) {
@@ -400,16 +425,40 @@ static int setup_input(const ExecContext *context, int socket_fd, bool apply_tty
}
}
-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) {
+static int setup_output(
+ Unit *unit,
+ const ExecContext *context,
+ const ExecParameters *params,
+ int fileno,
+ int socket_fd,
+ const char *ident,
+ uid_t uid, gid_t gid) {
+
ExecOutput o;
ExecInput i;
int r;
assert(unit);
assert(context);
+ assert(params);
assert(ident);
- i = fixup_input(context->std_input, socket_fd, apply_tty_stdin);
+ if (fileno == STDOUT_FILENO && params->stdout_fd >= 0) {
+
+ if (dup2(params->stdout_fd, STDOUT_FILENO) < 0)
+ return -errno;
+
+ return STDOUT_FILENO;
+ }
+
+ if (fileno == STDERR_FILENO && params->stderr_fd >= 0) {
+ if (dup2(params->stderr_fd, STDERR_FILENO) < 0)
+ return -errno;
+
+ return STDERR_FILENO;
+ }
+
+ i = fixup_input(context->std_input, socket_fd, params->apply_tty_stdin);
o = fixup_output(context->std_output, socket_fd);
if (fileno == STDERR_FILENO) {
@@ -502,9 +551,9 @@ static int chown_terminal(int fd, uid_t uid) {
return 0;
}
-static int setup_confirm_stdio(int *_saved_stdin,
- int *_saved_stdout) {
- int fd = -1, saved_stdin, saved_stdout = -1, r;
+static int setup_confirm_stdio(int *_saved_stdin, int *_saved_stdout) {
+ _cleanup_close_ int fd = -1, saved_stdin = -1, saved_stdout = -1;
+ int r;
assert(_saved_stdin);
assert(_saved_stdout);
@@ -514,10 +563,8 @@ static int setup_confirm_stdio(int *_saved_stdin,
return -errno;
saved_stdout = fcntl(STDOUT_FILENO, F_DUPFD, 3);
- if (saved_stdout < 0) {
- r = errno;
- goto fail;
- }
+ if (saved_stdout < 0)
+ return -errno;
fd = acquire_terminal(
"/dev/console",
@@ -525,39 +572,33 @@ static int setup_confirm_stdio(int *_saved_stdin,
false,
false,
DEFAULT_CONFIRM_USEC);
- if (fd < 0) {
- r = fd;
- goto fail;
- }
+ if (fd < 0)
+ return fd;
r = chown_terminal(fd, getuid());
if (r < 0)
- goto fail;
+ return r;
- if (dup2(fd, STDIN_FILENO) < 0) {
- r = -errno;
- goto fail;
- }
+ r = reset_terminal_fd(fd, true);
+ if (r < 0)
+ return r;
- if (dup2(fd, STDOUT_FILENO) < 0) {
- r = -errno;
- goto fail;
- }
+ if (dup2(fd, STDIN_FILENO) < 0)
+ return -errno;
+
+ if (dup2(fd, STDOUT_FILENO) < 0)
+ return -errno;
if (fd >= 2)
safe_close(fd);
+ fd = -1;
*_saved_stdin = saved_stdin;
*_saved_stdout = saved_stdout;
- return 0;
-
-fail:
- safe_close(saved_stdout);
- safe_close(saved_stdin);
- safe_close(fd);
+ saved_stdin = saved_stdout = -1;
- return r;
+ return 0;
}
_printf_(1, 2) static int write_confirm_message(const char *format, ...) {
@@ -577,9 +618,7 @@ _printf_(1, 2) static int write_confirm_message(const char *format, ...) {
return 0;
}
-static int restore_confirm_stdio(int *saved_stdin,
- int *saved_stdout) {
-
+static int restore_confirm_stdio(int *saved_stdin, int *saved_stdout) {
int r = 0;
assert(saved_stdin);
@@ -595,8 +634,8 @@ static int restore_confirm_stdio(int *saved_stdin,
if (dup2(*saved_stdout, STDOUT_FILENO) < 0)
r = -errno;
- safe_close(*saved_stdin);
- safe_close(*saved_stdout);
+ *saved_stdin = safe_close(*saved_stdin);
+ *saved_stdout = safe_close(*saved_stdout);
return r;
}
@@ -698,12 +737,7 @@ static int enforce_user(const ExecContext *context, uid_t uid) {
/* Sets (but doesn't lookup) the uid and make sure we keep the
* capabilities while doing so. */
- if (context->capabilities) {
- _cleanup_cap_free_ cap_t d = NULL;
- static const cap_value_t bits[] = {
- CAP_SETUID, /* Necessary so that we can run setresuid() below */
- CAP_SETPCAP /* Necessary so that we can set PR_SET_SECUREBITS later on */
- };
+ if (context->capabilities || context->capability_ambient_set != 0) {
/* First step: If we need to keep capabilities but
* drop privileges we need to make sure we keep our
@@ -719,16 +753,24 @@ static int enforce_user(const ExecContext *context, uid_t uid) {
/* Second step: set the capabilities. This will reduce
* the capabilities to the minimum we need. */
- d = cap_dup(context->capabilities);
- if (!d)
- return -errno;
+ if (context->capabilities) {
+ _cleanup_cap_free_ cap_t d = NULL;
+ static const cap_value_t bits[] = {
+ CAP_SETUID, /* Necessary so that we can run setresuid() below */
+ CAP_SETPCAP /* Necessary so that we can set PR_SET_SECUREBITS later on */
+ };
- if (cap_set_flag(d, CAP_EFFECTIVE, ELEMENTSOF(bits), bits, CAP_SET) < 0 ||
- cap_set_flag(d, CAP_PERMITTED, ELEMENTSOF(bits), bits, CAP_SET) < 0)
- return -errno;
+ d = cap_dup(context->capabilities);
+ if (!d)
+ return -errno;
- if (cap_set_proc(d) < 0)
- return -errno;
+ if (cap_set_flag(d, CAP_EFFECTIVE, ELEMENTSOF(bits), bits, CAP_SET) < 0 ||
+ cap_set_flag(d, CAP_PERMITTED, ELEMENTSOF(bits), bits, CAP_SET) < 0)
+ return -errno;
+
+ if (cap_set_proc(d) < 0)
+ return -errno;
+ }
}
/* Third step: actually set the uids */
@@ -949,14 +991,8 @@ fail:
}
strv_free(e);
-
closelog();
- if (pam_pid > 1) {
- kill(pam_pid, SIGTERM);
- kill(pam_pid, SIGCONT);
- }
-
return err;
}
#endif
@@ -1198,6 +1234,7 @@ static void do_idle_pipe_dance(int idle_pipe[4]) {
static int build_environment(
const ExecContext *c,
unsigned n_fds,
+ char ** fd_names,
usec_t watchdog_usec,
const char *home,
const char *username,
@@ -1211,11 +1248,13 @@ static int build_environment(
assert(c);
assert(ret);
- our_env = new0(char*, 10);
+ our_env = new0(char*, 11);
if (!our_env)
return -ENOMEM;
if (n_fds > 0) {
+ _cleanup_free_ char *joined = NULL;
+
if (asprintf(&x, "LISTEN_PID="PID_FMT, getpid()) < 0)
return -ENOMEM;
our_env[n_env++] = x;
@@ -1223,6 +1262,15 @@ static int build_environment(
if (asprintf(&x, "LISTEN_FDS=%u", n_fds) < 0)
return -ENOMEM;
our_env[n_env++] = x;
+
+ joined = strv_join(fd_names, ":");
+ if (!joined)
+ return -ENOMEM;
+
+ x = strjoin("LISTEN_FDNAMES=", joined, NULL);
+ if (!x)
+ return -ENOMEM;
+ our_env[n_env++] = x;
}
if (watchdog_usec > 0) {
@@ -1273,7 +1321,7 @@ static int build_environment(
}
our_env[n_env++] = NULL;
- assert(n_env <= 10);
+ assert(n_env <= 11);
*ret = our_env;
our_env = NULL;
@@ -1281,6 +1329,34 @@ static int build_environment(
return 0;
}
+static int build_pass_environment(const ExecContext *c, char ***ret) {
+ _cleanup_strv_free_ char **pass_env = NULL;
+ size_t n_env = 0, n_bufsize = 0;
+ char **i;
+
+ STRV_FOREACH(i, c->pass_environment) {
+ _cleanup_free_ char *x = NULL;
+ char *v;
+
+ v = getenv(*i);
+ if (!v)
+ continue;
+ x = strjoin(*i, "=", v, NULL);
+ if (!x)
+ return -ENOMEM;
+ if (!GREEDY_REALLOC(pass_env, n_bufsize, n_env + 2))
+ return -ENOMEM;
+ pass_env[n_env++] = x;
+ pass_env[n_env] = NULL;
+ x = NULL;
+ }
+
+ *ret = pass_env;
+ pass_env = NULL;
+
+ return 0;
+}
+
static bool exec_needs_mount_namespace(
const ExecContext *context,
const ExecParameters *params,
@@ -1311,6 +1387,44 @@ static bool exec_needs_mount_namespace(
return false;
}
+static int close_remaining_fds(
+ const ExecParameters *params,
+ ExecRuntime *runtime,
+ int socket_fd,
+ int *fds, unsigned n_fds) {
+
+ unsigned n_dont_close = 0;
+ int dont_close[n_fds + 7];
+
+ assert(params);
+
+ if (params->stdin_fd >= 0)
+ dont_close[n_dont_close++] = params->stdin_fd;
+ if (params->stdout_fd >= 0)
+ dont_close[n_dont_close++] = params->stdout_fd;
+ if (params->stderr_fd >= 0)
+ dont_close[n_dont_close++] = params->stderr_fd;
+
+ if (socket_fd >= 0)
+ dont_close[n_dont_close++] = socket_fd;
+ if (n_fds > 0) {
+ memcpy(dont_close + n_dont_close, fds, sizeof(int) * n_fds);
+ n_dont_close += n_fds;
+ }
+
+ if (params->bus_endpoint_fd >= 0)
+ dont_close[n_dont_close++] = params->bus_endpoint_fd;
+
+ if (runtime) {
+ if (runtime->netns_storage_socket[0] >= 0)
+ dont_close[n_dont_close++] = runtime->netns_storage_socket[0];
+ if (runtime->netns_storage_socket[1] >= 0)
+ dont_close[n_dont_close++] = runtime->netns_storage_socket[1];
+ }
+
+ return close_all_fds(dont_close, n_dont_close);
+}
+
static int exec_child(
Unit *unit,
ExecCommand *command,
@@ -1323,11 +1437,9 @@ static int exec_child(
char **files_env,
int *exit_status) {
- _cleanup_strv_free_ char **our_env = NULL, **pam_env = NULL, **final_env = NULL, **final_argv = NULL;
+ _cleanup_strv_free_ char **our_env = NULL, **pass_env = NULL, **pam_env = NULL, **final_env = NULL, **final_argv = NULL;
_cleanup_free_ char *mac_selinux_context_net = NULL;
- const char *username = NULL, *home = NULL, *shell = NULL;
- unsigned n_dont_close = 0;
- int dont_close[n_fds + 4];
+ const char *username = NULL, *home = NULL, *shell = NULL, *wd;
uid_t uid = UID_INVALID;
gid_t gid = GID_INVALID;
int i, r;
@@ -1367,22 +1479,7 @@ static int exec_child(
log_forget_fds();
- if (socket_fd >= 0)
- dont_close[n_dont_close++] = socket_fd;
- if (n_fds > 0) {
- memcpy(dont_close + n_dont_close, fds, sizeof(int) * n_fds);
- n_dont_close += n_fds;
- }
- if (params->bus_endpoint_fd >= 0)
- dont_close[n_dont_close++] = params->bus_endpoint_fd;
- if (runtime) {
- if (runtime->netns_storage_socket[0] >= 0)
- dont_close[n_dont_close++] = runtime->netns_storage_socket[0];
- if (runtime->netns_storage_socket[1] >= 0)
- dont_close[n_dont_close++] = runtime->netns_storage_socket[1];
- }
-
- r = close_all_fds(dont_close, n_dont_close);
+ r = close_remaining_fds(params, runtime, socket_fd, fds, n_fds);
if (r < 0) {
*exit_status = EXIT_FDS;
return r;
@@ -1438,21 +1535,21 @@ static int exec_child(
/* If a socket is connected to STDIN/STDOUT/STDERR, we
* must sure to drop O_NONBLOCK */
if (socket_fd >= 0)
- fd_nonblock(socket_fd, false);
+ (void) fd_nonblock(socket_fd, false);
- r = setup_input(context, socket_fd, params->apply_tty_stdin);
+ r = setup_input(context, params, socket_fd);
if (r < 0) {
*exit_status = EXIT_STDIN;
return r;
}
- r = setup_output(unit, context, STDOUT_FILENO, socket_fd, basename(command->path), params->apply_tty_stdin, uid, gid);
+ r = setup_output(unit, context, params, STDOUT_FILENO, socket_fd, basename(command->path), uid, gid);
if (r < 0) {
*exit_status = EXIT_STDOUT;
return r;
}
- r = setup_output(unit, context, STDERR_FILENO, socket_fd, basename(command->path), params->apply_tty_stdin, uid, gid);
+ r = setup_output(unit, context, params, STDERR_FILENO, socket_fd, basename(command->path), uid, gid);
if (r < 0) {
*exit_status = EXIT_STDERR;
return r;
@@ -1698,6 +1795,13 @@ static int exec_child(
}
}
+ if (context->working_directory_home)
+ wd = home;
+ else if (context->working_directory)
+ wd = context->working_directory;
+ else
+ wd = "/";
+
if (params->apply_chroot) {
if (!needs_mount_namespace && context->root_directory)
if (chroot(context->root_directory) < 0) {
@@ -1705,21 +1809,15 @@ static int exec_child(
return -errno;
}
- if (chdir(context->working_directory ?: "/") < 0 &&
+ if (chdir(wd) < 0 &&
!context->working_directory_missing_ok) {
*exit_status = EXIT_CHDIR;
return -errno;
}
} else {
- _cleanup_free_ char *d = NULL;
-
- if (asprintf(&d, "%s/%s",
- context->root_directory ?: "",
- context->working_directory ?: "") < 0) {
- *exit_status = EXIT_MEMORY;
- return -ENOMEM;
- }
+ const char *d;
+ d = strjoina(strempty(context->root_directory), "/", strempty(wd));
if (chdir(d) < 0 &&
!context->working_directory_missing_ok) {
*exit_status = EXIT_CHDIR;
@@ -1755,6 +1853,8 @@ static int exec_child(
if (params->apply_permissions) {
+ int secure_bits = context->secure_bits;
+
for (i = 0; i < _RLIMIT_MAX; i++) {
if (!context->rlimit[i])
continue;
@@ -1765,28 +1865,71 @@ static int exec_child(
}
}
- if (context->capability_bounding_set_drop) {
- r = capability_bounding_set_drop(context->capability_bounding_set_drop, false);
+ if (!cap_test_all(context->capability_bounding_set)) {
+ r = capability_bounding_set_drop(context->capability_bounding_set, false);
if (r < 0) {
*exit_status = EXIT_CAPABILITIES;
return r;
}
}
+ /* This is done before enforce_user, but ambient set
+ * does not survive over setresuid() if keep_caps is not set. */
+ if (context->capability_ambient_set != 0) {
+ r = capability_ambient_set_apply(context->capability_ambient_set, true);
+ if (r < 0) {
+ *exit_status = EXIT_CAPABILITIES;
+ return r;
+ }
+
+ if (context->capabilities) {
+
+ /* The capabilities in ambient set need to be also in the inherited
+ * set. If they aren't, trying to get them will fail. Add the ambient
+ * set inherited capabilities to the capability set in the context.
+ * This is needed because if capabilities are set (using "Capabilities="
+ * keyword), they will override whatever we set now. */
+
+ r = capability_update_inherited_set(context->capabilities, context->capability_ambient_set);
+ if (r < 0) {
+ *exit_status = EXIT_CAPABILITIES;
+ return r;
+ }
+ }
+ }
+
if (context->user) {
r = enforce_user(context, uid);
if (r < 0) {
*exit_status = EXIT_USER;
return r;
}
+ if (context->capability_ambient_set != 0) {
+
+ /* Fix the ambient capabilities after user change. */
+ r = capability_ambient_set_apply(context->capability_ambient_set, false);
+ if (r < 0) {
+ *exit_status = EXIT_CAPABILITIES;
+ return r;
+ }
+
+ /* If we were asked to change user and ambient capabilities
+ * were requested, we had to add keep-caps to the securebits
+ * so that we would maintain the inherited capability set
+ * through the setresuid(). Make sure that the bit is added
+ * also to the context secure_bits so that we don't try to
+ * drop the bit away next. */
+
+ secure_bits |= 1<<SECURE_KEEP_CAPS;
+ }
}
/* PR_GET_SECUREBITS is not privileged, while
* PR_SET_SECUREBITS is. So to suppress
* potential EPERMs we'll try not to call
* PR_SET_SECUREBITS unless necessary. */
- if (prctl(PR_GET_SECUREBITS) != context->secure_bits)
- if (prctl(PR_SET_SECUREBITS, context->secure_bits) < 0) {
+ if (prctl(PR_GET_SECUREBITS) != secure_bits)
+ if (prctl(PR_SET_SECUREBITS, secure_bits) < 0) {
*exit_status = EXIT_SECUREBITS;
return -errno;
}
@@ -1849,15 +1992,22 @@ static int exec_child(
#endif
}
- r = build_environment(context, n_fds, params->watchdog_usec, home, username, shell, &our_env);
+ r = build_environment(context, n_fds, params->fd_names, params->watchdog_usec, home, username, shell, &our_env);
+ if (r < 0) {
+ *exit_status = EXIT_MEMORY;
+ return r;
+ }
+
+ r = build_pass_environment(context, &pass_env);
if (r < 0) {
*exit_status = EXIT_MEMORY;
return r;
}
- final_env = strv_env_merge(5,
+ final_env = strv_env_merge(6,
params->environment,
our_env,
+ pass_env,
context->environment,
files_env,
pam_env,
@@ -1948,7 +2098,7 @@ int exec_spawn(Unit *unit,
NULL);
pid = fork();
if (pid < 0)
- return log_unit_error_errno(unit, r, "Failed to fork: %m");
+ return log_unit_error_errno(unit, errno, "Failed to fork: %m");
if (pid == 0) {
int exit_status;
@@ -2006,6 +2156,7 @@ void exec_context_init(ExecContext *c) {
c->timer_slack_nsec = NSEC_INFINITY;
c->personality = PERSONALITY_INVALID;
c->runtime_directory_mode = 0755;
+ c->capability_bounding_set = CAP_ALL;
}
void exec_context_done(ExecContext *c) {
@@ -2015,6 +2166,7 @@ void exec_context_done(ExecContext *c) {
c->environment = strv_free(c->environment);
c->environment_files = strv_free(c->environment_files);
+ c->pass_environment = strv_free(c->pass_environment);
for (l = 0; l < ELEMENTSOF(c->rlimit); l++)
c->rlimit[l] = mfree(c->rlimit[l]);
@@ -2161,7 +2313,7 @@ int exec_context_load_environment(Unit *unit, const ExecContext *c, char ***l) {
continue;
strv_free(r);
- return errno ? -errno : -EINVAL;
+ return errno > 0 ? -errno : -EINVAL;
}
count = pglob.gl_pathc;
if (count == 0) {
@@ -2249,7 +2401,7 @@ static void strv_fprintf(FILE *f, char **l) {
}
void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
- char **e;
+ char **e, **d;
unsigned i;
assert(c);
@@ -2285,6 +2437,14 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
STRV_FOREACH(e, c->environment_files)
fprintf(f, "%sEnvironmentFile: %s\n", prefix, *e);
+ STRV_FOREACH(e, c->pass_environment)
+ fprintf(f, "%sPassEnvironment: %s\n", prefix, *e);
+
+ fprintf(f, "%sRuntimeDirectoryMode: %04o\n", prefix, c->runtime_directory_mode);
+
+ STRV_FOREACH(d, c->runtime_directory)
+ fprintf(f, "%sRuntimeDirectory: %s\n", prefix, *d);
+
if (c->nice_set)
fprintf(f,
"%sNice: %i\n",
@@ -2296,9 +2456,12 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
prefix, c->oom_score_adjust);
for (i = 0; i < RLIM_NLIMITS; i++)
- if (c->rlimit[i])
- fprintf(f, "%s%s: "RLIM_FMT"\n",
+ if (c->rlimit[i]) {
+ fprintf(f, "%s%s: " RLIM_FMT "\n",
prefix, rlimit_to_string(i), c->rlimit[i]->rlim_max);
+ fprintf(f, "%s%sSoft: " RLIM_FMT "\n",
+ prefix, rlimit_to_string(i), c->rlimit[i]->rlim_cur);
+ }
if (c->ioprio_set) {
_cleanup_free_ char *class_str = NULL;
@@ -2397,12 +2560,23 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
(c->secure_bits & 1<<SECURE_NOROOT) ? " noroot" : "",
(c->secure_bits & 1<<SECURE_NOROOT_LOCKED) ? "noroot-locked" : "");
- if (c->capability_bounding_set_drop) {
+ if (c->capability_bounding_set != CAP_ALL) {
unsigned long l;
fprintf(f, "%sCapabilityBoundingSet:", prefix);
for (l = 0; l <= cap_last_cap(); l++)
- if (!(c->capability_bounding_set_drop & ((uint64_t) 1ULL << (uint64_t) l)))
+ if (c->capability_bounding_set & (UINT64_C(1) << l))
+ fprintf(f, " %s", strna(capability_to_name(l)));
+
+ fputs("\n", f);
+ }
+
+ if (c->capability_ambient_set != 0) {
+ unsigned long l;
+ fprintf(f, "%sAmbientCapabilities:", prefix);
+
+ for (l = 0; l <= cap_last_cap(); l++)
+ if (c->capability_ambient_set & (UINT64_C(1) << l))
fprintf(f, " %s", strna(capability_to_name(l)));
fputs("\n", f);
@@ -2503,7 +2677,7 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
fputc('\n', f);
}
- if (c->syscall_errno != 0)
+ if (c->syscall_errno > 0)
fprintf(f,
"%sSystemCallErrorNumber: %s\n",
prefix, strna(errno_to_name(c->syscall_errno)));
@@ -2711,7 +2885,7 @@ int exec_command_append(ExecCommand *c, const char *path, ...) {
if (!l)
return -ENOMEM;
- r = strv_extend_strv(&c->argv, l);
+ r = strv_extend_strv(&c->argv, l, false);
if (r < 0)
return r;
diff --git a/src/core/execute.h b/src/core/execute.h
index a750246a89..8649620830 100644
--- a/src/core/execute.h
+++ b/src/core/execute.h
@@ -27,16 +27,16 @@ typedef struct ExecContext ExecContext;
typedef struct ExecRuntime ExecRuntime;
typedef struct ExecParameters ExecParameters;
-#include <sys/capability.h>
+#include <sched.h>
#include <stdbool.h>
#include <stdio.h>
-#include <sched.h>
+#include <sys/capability.h>
-#include "list.h"
+#include "bus-endpoint.h"
#include "fdset.h"
+#include "list.h"
#include "missing.h"
#include "namespace.h"
-#include "bus-endpoint.h"
typedef enum ExecUtmpMode {
EXEC_UTMP_INIT,
@@ -99,10 +99,12 @@ struct ExecRuntime {
struct ExecContext {
char **environment;
char **environment_files;
+ char **pass_environment;
struct rlimit *rlimit[_RLIMIT_MAX];
char *working_directory, *root_directory;
bool working_directory_missing_ok;
+ bool working_directory_home;
mode_t umask;
int oom_score_adjust;
@@ -153,7 +155,9 @@ struct ExecContext {
char **read_write_dirs, **read_only_dirs, **inaccessible_dirs;
unsigned long mount_flags;
- uint64_t capability_bounding_set_drop;
+ uint64_t capability_bounding_set;
+
+ uint64_t capability_ambient_set;
cap_t capabilities;
int secure_bits;
@@ -202,26 +206,40 @@ struct ExecContext {
BusEndpoint *bus_endpoint;
};
-#include "cgroup.h"
#include "cgroup-util.h"
+#include "cgroup.h"
struct ExecParameters {
char **argv;
- int *fds; unsigned n_fds;
char **environment;
- bool apply_permissions;
- bool apply_chroot;
- bool apply_tty_stdin;
- bool confirm_spawn;
- bool selinux_context_net;
+
+ int *fds;
+ char **fd_names;
+ unsigned n_fds;
+
+ bool apply_permissions:1;
+ bool apply_chroot:1;
+ bool apply_tty_stdin:1;
+
+ bool confirm_spawn:1;
+ bool selinux_context_net:1;
+
+ bool cgroup_delegate:1;
CGroupMask cgroup_supported;
const char *cgroup_path;
- bool cgroup_delegate;
+
const char *runtime_prefix;
+
usec_t watchdog_usec;
+
int *idle_pipe;
+
char *bus_endpoint_path;
int bus_endpoint_fd;
+
+ int stdin_fd;
+ int stdout_fd;
+ int stderr_fd;
};
int exec_spawn(Unit *unit,
diff --git a/src/core/failure-action.c b/src/core/failure-action.c
index 3412accf3e..f67fb05af0 100644
--- a/src/core/failure-action.c
+++ b/src/core/failure-action.c
@@ -23,10 +23,11 @@
#include <sys/reboot.h>
#include <linux/reboot.h>
-#include "bus-util.h"
#include "bus-error.h"
-#include "special.h"
+#include "bus-util.h"
#include "failure-action.h"
+#include "special.h"
+#include "string-table.h"
#include "terminal-util.h"
static void log_and_status(Manager *m, const char *message) {
@@ -41,8 +42,6 @@ int failure_action(
FailureAction action,
const char *reboot_arg) {
- int r;
-
assert(m);
assert(action >= 0);
assert(action < _FAILURE_ACTION_MAX);
@@ -61,18 +60,13 @@ int failure_action(
switch (action) {
- case FAILURE_ACTION_REBOOT: {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
-
+ case FAILURE_ACTION_REBOOT:
log_and_status(m, "Rebooting as result of failure.");
update_reboot_param_file(reboot_arg);
- r = manager_add_job_by_name(m, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE, true, &error, NULL);
- if (r < 0)
- log_error("Failed to reboot: %s.", bus_error_message(&error, r));
+ (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE, NULL);
break;
- }
case FAILURE_ACTION_REBOOT_FORCE:
log_and_status(m, "Forcibly rebooting as result of failure.");
@@ -95,17 +89,10 @@ int failure_action(
reboot(RB_AUTOBOOT);
break;
- case FAILURE_ACTION_POWEROFF: {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
-
+ case FAILURE_ACTION_POWEROFF:
log_and_status(m, "Powering off as result of failure.");
-
- r = manager_add_job_by_name(m, JOB_START, SPECIAL_POWEROFF_TARGET, JOB_REPLACE, true, &error, NULL);
- if (r < 0)
- log_error("Failed to poweroff: %s.", bus_error_message(&error, r));
-
+ (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_POWEROFF_TARGET, JOB_REPLACE, NULL);
break;
- }
case FAILURE_ACTION_POWEROFF_FORCE:
log_and_status(m, "Forcibly powering off as result of failure.");
diff --git a/src/core/hostname-setup.c b/src/core/hostname-setup.c
index 932ddbf95a..d92a9a764f 100644
--- a/src/core/hostname-setup.c
+++ b/src/core/hostname-setup.c
@@ -19,16 +19,18 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
#include <errno.h>
+#include <stdio.h>
#include <stdlib.h>
-#include "macro.h"
-#include "util.h"
-#include "log.h"
+#include "alloc-util.h"
#include "fileio.h"
-#include "hostname-util.h"
#include "hostname-setup.h"
+#include "hostname-util.h"
+#include "log.h"
+#include "macro.h"
+#include "string-util.h"
+#include "util.h"
int hostname_setup(void) {
int r;
@@ -59,8 +61,9 @@ int hostname_setup(void) {
hn = "localhost";
}
- if (sethostname_idempotent(hn) < 0)
- return log_warning_errno(errno, "Failed to set hostname to <%s>: %m", hn);
+ r = sethostname_idempotent(hn);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to set hostname to <%s>: %m", hn);
log_info("Set hostname to <%s>.", hn);
return 0;
diff --git a/src/core/ima-setup.c b/src/core/ima-setup.c
index 42a3e97459..4f42ae6f31 100644
--- a/src/core/ima-setup.c
+++ b/src/core/ima-setup.c
@@ -21,12 +21,14 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
#include <errno.h>
+#include <unistd.h>
+#include "fd-util.h"
+#include "fileio.h"
#include "ima-setup.h"
-#include "util.h"
#include "log.h"
+#include "util.h"
#define IMA_SECFS_DIR "/sys/kernel/security/ima"
#define IMA_SECFS_POLICY IMA_SECFS_DIR "/policy"
diff --git a/src/core/job.c b/src/core/job.c
index 558d8d2d52..274c554da9 100644
--- a/src/core/job.c
+++ b/src/core/job.c
@@ -23,17 +23,25 @@
#include "sd-id128.h"
#include "sd-messages.h"
-#include "set.h"
-#include "unit.h"
-#include "macro.h"
-#include "strv.h"
-#include "log.h"
-#include "dbus-job.h"
-#include "special.h"
+
+#include "alloc-util.h"
#include "async.h"
-#include "virt.h"
+#include "dbus-job.h"
#include "dbus.h"
+#include "escape.h"
+#include "job.h"
+#include "log.h"
+#include "macro.h"
+#include "parse-util.h"
+#include "set.h"
+#include "special.h"
+#include "stdio-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "strv.h"
#include "terminal-util.h"
+#include "unit.h"
+#include "virt.h"
Job* job_new_raw(Unit *unit) {
Job *j;
@@ -168,7 +176,6 @@ static void job_merge_into_installed(Job *j, Job *other) {
else
assert(other->type == JOB_NOP);
- j->override = j->override || other->override;
j->irreversible = j->irreversible || other->irreversible;
j->ignore_order = j->ignore_order || other->ignore_order;
}
@@ -300,12 +307,10 @@ void job_dump(Job *j, FILE*f, const char *prefix) {
"%s-> Job %u:\n"
"%s\tAction: %s -> %s\n"
"%s\tState: %s\n"
- "%s\tForced: %s\n"
"%s\tIrreversible: %s\n",
prefix, j->id,
prefix, j->unit->id, job_type_to_string(j->type),
prefix, job_state_to_string(j->state),
- prefix, yes_no(j->override),
prefix, yes_no(j->irreversible));
}
@@ -496,17 +501,26 @@ static void job_change_type(Job *j, JobType newtype) {
}
static int job_perform_on_unit(Job **j) {
- /* While we execute this operation the job might go away (for
- * example: because it finishes immediately or is replaced by a new,
- * conflicting job.) To make sure we don't access a freed job later on
- * we store the id here, so that we can verify the job is still
- * valid. */
- Manager *m = (*j)->manager;
- Unit *u = (*j)->unit;
- JobType t = (*j)->type;
- uint32_t id = (*j)->id;
+ uint32_t id;
+ Manager *m;
+ JobType t;
+ Unit *u;
int r;
+ /* While we execute this operation the job might go away (for
+ * example: because it finishes immediately or is replaced by
+ * a new, conflicting job.) To make sure we don't access a
+ * freed job later on we store the id here, so that we can
+ * verify the job is still valid. */
+
+ assert(j);
+ assert(*j);
+
+ m = (*j)->manager;
+ u = (*j)->unit;
+ t = (*j)->type;
+ id = (*j)->id;
+
switch (t) {
case JOB_START:
r = unit_start(u);
@@ -514,6 +528,7 @@ static int job_perform_on_unit(Job **j) {
case JOB_RESTART:
t = JOB_STOP;
+ /* fall through */
case JOB_STOP:
r = unit_stop(u);
break;
@@ -613,8 +628,7 @@ int job_run_and_invalidate(Job *j) {
}
_pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobResult result) {
- const char *format;
- const UnitStatusMessageFormats *format_table;
+
static const char *const generic_finished_start_job[_JOB_RESULT_MAX] = {
[JOB_DONE] = "Started %s.",
[JOB_TIMEOUT] = "Timed out starting %s.",
@@ -640,11 +654,14 @@ _pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobR
[JOB_SKIPPED] = "%s is not active.",
};
+ const UnitStatusMessageFormats *format_table;
+ const char *format;
+
assert(u);
assert(t >= 0);
assert(t < _JOB_TYPE_MAX);
- if (t == JOB_START || t == JOB_STOP || t == JOB_RESTART) {
+ if (IN_SET(t, JOB_START, JOB_STOP, JOB_RESTART)) {
format_table = &UNIT_VTABLE(u)->status_message_formats;
if (format_table) {
format = t == JOB_START ? format_table->finished_start_job[result] :
@@ -668,7 +685,6 @@ _pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobR
}
static void job_print_status_message(Unit *u, JobType t, JobResult result) {
- const char *format;
static const char* const job_result_status_table[_JOB_RESULT_MAX] = {
[JOB_DONE] = ANSI_GREEN " OK " ANSI_NORMAL,
[JOB_TIMEOUT] = ANSI_HIGHLIGHT_RED " TIME " ANSI_NORMAL,
@@ -679,10 +695,16 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) {
[JOB_UNSUPPORTED] = ANSI_HIGHLIGHT_YELLOW "UNSUPP" ANSI_NORMAL,
};
+ const char *format;
+
assert(u);
assert(t >= 0);
assert(t < _JOB_TYPE_MAX);
+ /* Reload status messages have traditionally not been printed to console. */
+ if (t == JOB_RELOAD)
+ return;
+
format = job_get_status_message_format(u, t, result);
if (!format)
return;
@@ -695,10 +717,10 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) {
REENABLE_WARNING;
if (t == JOB_START && result == JOB_FAILED) {
- _cleanup_free_ char *quoted = shell_maybe_quote(u->id);
+ _cleanup_free_ char *quoted;
- manager_status_printf(u->manager, STATUS_TYPE_NORMAL, NULL,
- "See 'systemctl status %s' for details.", strna(quoted));
+ quoted = shell_maybe_quote(u->id);
+ manager_status_printf(u->manager, STATUS_TYPE_NORMAL, NULL, "See 'systemctl status %s' for details.", strna(quoted));
}
}
@@ -733,16 +755,25 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) {
return;
DISABLE_WARNING_FORMAT_NONLITERAL;
- snprintf(buf, sizeof(buf), format, unit_description(u));
+ xsprintf(buf, format, unit_description(u));
REENABLE_WARNING;
- if (t == JOB_START)
+ switch (t) {
+
+ case JOB_START:
mid = result == JOB_DONE ? SD_MESSAGE_UNIT_STARTED : SD_MESSAGE_UNIT_FAILED;
- else if (t == JOB_STOP || t == JOB_RESTART)
- mid = SD_MESSAGE_UNIT_STOPPED;
- else if (t == JOB_RELOAD)
+ break;
+
+ case JOB_RELOAD:
mid = SD_MESSAGE_UNIT_RELOADED;
- else {
+ break;
+
+ case JOB_STOP:
+ case JOB_RESTART:
+ mid = SD_MESSAGE_UNIT_STOPPED;
+ break;
+
+ default:
log_struct(job_result_log_level[result],
LOG_UNIT_ID(u),
LOG_MESSAGE("%s", buf),
@@ -766,10 +797,7 @@ static void job_emit_status_message(Unit *u, JobType t, JobResult result) {
return;
job_log_status_message(u, t, result);
-
- /* Reload status messages have traditionally not been printed to console. */
- if (t != JOB_RELOAD)
- job_print_status_message(u, t, result);
+ job_print_status_message(u, t, result);
}
static void job_fail_dependencies(Unit *u, UnitDependency d) {
@@ -834,8 +862,6 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) {
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);
}
@@ -960,7 +986,6 @@ int job_serialize(Job *j, FILE *f, FDSet *fds) {
fprintf(f, "job-id=%u\n", j->id);
fprintf(f, "job-type=%s\n", job_type_to_string(j->type));
fprintf(f, "job-state=%s\n", job_state_to_string(j->state));
- fprintf(f, "job-override=%s\n", yes_no(j->override));
fprintf(f, "job-irreversible=%s\n", yes_no(j->irreversible));
fprintf(f, "job-sent-dbus-new-signal=%s\n", yes_no(j->sent_dbus_new_signal));
fprintf(f, "job-ignore-order=%s\n", yes_no(j->ignore_order));
@@ -1028,15 +1053,6 @@ int job_deserialize(Job *j, FILE *f, FDSet *fds) {
else
job_set_state(j, s);
- } else if (streq(l, "job-override")) {
- int b;
-
- b = parse_boolean(v);
- if (b < 0)
- log_debug("Failed to parse job override flag %s", v);
- else
- j->override = j->override || b;
-
} else if (streq(l, "job-irreversible")) {
int b;
diff --git a/src/core/job.h b/src/core/job.h
index 1d1b10f1d3..118b24e5b7 100644
--- a/src/core/job.h
+++ b/src/core/job.h
@@ -23,6 +23,11 @@
#include <stdbool.h>
+#include "sd-event.h"
+
+#include "list.h"
+#include "unit-name.h"
+
typedef struct Job Job;
typedef struct JobDependency JobDependency;
typedef enum JobType JobType;
@@ -105,9 +110,7 @@ enum JobResult {
_JOB_RESULT_INVALID = -1
};
-#include "sd-event.h"
#include "unit.h"
-#include "list.h"
struct JobDependency {
/* Encodes that the 'subject' job needs the 'object' job in
@@ -160,7 +163,6 @@ struct Job {
bool installed:1;
bool in_run_queue:1;
bool matters_to_anchor:1;
- bool override:1;
bool in_dbus_queue:1;
bool sent_dbus_new_signal:1;
bool ignore_order:1;
diff --git a/src/core/kill.c b/src/core/kill.c
index bddfa4460f..1466d5ce64 100644
--- a/src/core/kill.c
+++ b/src/core/kill.c
@@ -19,9 +19,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
-#include "signal-util.h"
#include "kill.h"
+#include "signal-util.h"
+#include "string-table.h"
+#include "util.h"
void kill_context_init(KillContext *c) {
assert(c);
diff --git a/src/core/killall.c b/src/core/killall.c
index ee5d388560..77f145b4d1 100644
--- a/src/core/killall.c
+++ b/src/core/killall.c
@@ -19,17 +19,21 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/wait.h>
-#include <signal.h>
#include <errno.h>
+#include <signal.h>
+#include <sys/wait.h>
#include <unistd.h>
-#include "util.h"
-#include "killall.h"
-#include "set.h"
+#include "alloc-util.h"
+#include "fd-util.h"
#include "formats-util.h"
+#include "killall.h"
+#include "parse-util.h"
#include "process-util.h"
+#include "set.h"
+#include "string-util.h"
#include "terminal-util.h"
+#include "util.h"
#define TIMEOUT_USEC (10 * USEC_PER_SEC)
diff --git a/src/core/kmod-setup.c b/src/core/kmod-setup.c
index 2068ffd69b..a6ab8cf4b3 100644
--- a/src/core/kmod-setup.c
+++ b/src/core/kmod-setup.c
@@ -19,17 +19,17 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
#include <string.h>
+#include <unistd.h>
#ifdef HAVE_KMOD
#include <libkmod.h>
#endif
-#include "macro.h"
-#include "capability.h"
#include "bus-util.h"
+#include "capability-util.h"
#include "kmod-setup.h"
+#include "macro.h"
#ifdef HAVE_KMOD
static void systemd_kmod_log(
diff --git a/src/core/load-dropin.c b/src/core/load-dropin.c
index 11566af51b..569632e13b 100644
--- a/src/core/load-dropin.c
+++ b/src/core/load-dropin.c
@@ -20,13 +20,13 @@
***/
-#include "unit.h"
+#include "conf-parser.h"
#include "load-dropin.h"
+#include "load-fragment.h"
#include "log.h"
#include "strv.h"
#include "unit-name.h"
-#include "conf-parser.h"
-#include "load-fragment.h"
+#include "unit.h"
static int add_dependency_consumer(
UnitDependency dependency,
@@ -65,6 +65,7 @@ int unit_load_dropin(Unit *u) {
}
}
+ u->dropin_paths = strv_free(u->dropin_paths);
r = unit_find_dropin_paths(u, &u->dropin_paths);
if (r <= 0)
return 0;
diff --git a/src/core/load-dropin.h b/src/core/load-dropin.h
index 1e018c4525..93ffcc4a72 100644
--- a/src/core/load-dropin.h
+++ b/src/core/load-dropin.h
@@ -21,8 +21,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "unit.h"
#include "dropin.h"
+#include "unit.h"
/* Read service data supplementary drop-in directories */
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index fd293d8287..29ab1b6b9e 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -17,7 +17,7 @@ struct ConfigPerfItem;
%%
m4_dnl Define the context options only once
m4_define(`EXEC_CONTEXT_CONFIG_ITEMS',
-`$1.WorkingDirectory, config_parse_unit_path_printf, 0, offsetof($1, exec_context.working_directory)
+`$1.WorkingDirectory, config_parse_working_directory, 0, offsetof($1, exec_context)
$1.RootDirectory, config_parse_unit_path_printf, 0, offsetof($1, exec_context.root_directory)
$1.User, config_parse_unit_string_printf, 0, offsetof($1, exec_context.user)
$1.Group, config_parse_unit_string_printf, 0, offsetof($1, exec_context.group)
@@ -33,6 +33,7 @@ $1.CPUAffinity, config_parse_exec_cpu_affinity, 0,
$1.UMask, config_parse_mode, 0, offsetof($1, exec_context.umask)
$1.Environment, config_parse_environ, 0, offsetof($1, exec_context.environment)
$1.EnvironmentFile, config_parse_unit_env_file, 0, offsetof($1, exec_context.environment_files)
+$1.PassEnvironment, config_parse_pass_environ, 0, offsetof($1, exec_context.pass_environment)
$1.StandardInput, config_parse_input, 0, offsetof($1, exec_context.std_input)
$1.StandardOutput, config_parse_output, 0, offsetof($1, exec_context.std_output)
$1.StandardError, config_parse_output, 0, offsetof($1, exec_context.std_error)
@@ -46,7 +47,8 @@ $1.SyslogLevel, config_parse_log_level, 0,
$1.SyslogLevelPrefix, config_parse_bool, 0, offsetof($1, exec_context.syslog_level_prefix)
$1.Capabilities, config_parse_exec_capabilities, 0, offsetof($1, exec_context)
$1.SecureBits, config_parse_exec_secure_bits, 0, offsetof($1, exec_context)
-$1.CapabilityBoundingSet, config_parse_bounding_set, 0, offsetof($1, exec_context.capability_bounding_set_drop)
+$1.CapabilityBoundingSet, config_parse_capability_set, 0, offsetof($1, exec_context.capability_bounding_set)
+$1.AmbientCapabilities, config_parse_capability_set, 0, offsetof($1, exec_context.capability_ambient_set)
$1.TimerSlackNSec, config_parse_nsec, 0, offsetof($1, exec_context.timer_slack_nsec)
$1.NoNewPrivileges, config_parse_no_new_privileges, 0, offsetof($1, exec_context)
m4_ifdef(`HAVE_SECCOMP',
@@ -58,22 +60,22 @@ $1.RestrictAddressFamilies, config_parse_address_families, 0,
$1.SystemCallArchitectures, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
$1.SystemCallErrorNumber, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
$1.RestrictAddressFamilies, config_parse_warn_compat, DISABLED_CONFIGURATION, 0')
-$1.LimitCPU, config_parse_limit, RLIMIT_CPU, offsetof($1, exec_context.rlimit)
-$1.LimitFSIZE, config_parse_limit, RLIMIT_FSIZE, offsetof($1, exec_context.rlimit)
-$1.LimitDATA, config_parse_limit, RLIMIT_DATA, offsetof($1, exec_context.rlimit)
-$1.LimitSTACK, config_parse_limit, RLIMIT_STACK, offsetof($1, exec_context.rlimit)
-$1.LimitCORE, config_parse_limit, RLIMIT_CORE, offsetof($1, exec_context.rlimit)
-$1.LimitRSS, config_parse_limit, RLIMIT_RSS, offsetof($1, exec_context.rlimit)
+$1.LimitCPU, config_parse_sec_limit, RLIMIT_CPU, offsetof($1, exec_context.rlimit)
+$1.LimitFSIZE, config_parse_bytes_limit, RLIMIT_FSIZE, offsetof($1, exec_context.rlimit)
+$1.LimitDATA, config_parse_bytes_limit, RLIMIT_DATA, offsetof($1, exec_context.rlimit)
+$1.LimitSTACK, config_parse_bytes_limit, RLIMIT_STACK, offsetof($1, exec_context.rlimit)
+$1.LimitCORE, config_parse_bytes_limit, RLIMIT_CORE, offsetof($1, exec_context.rlimit)
+$1.LimitRSS, config_parse_bytes_limit, RLIMIT_RSS, offsetof($1, exec_context.rlimit)
$1.LimitNOFILE, config_parse_limit, RLIMIT_NOFILE, offsetof($1, exec_context.rlimit)
-$1.LimitAS, config_parse_limit, RLIMIT_AS, offsetof($1, exec_context.rlimit)
+$1.LimitAS, config_parse_bytes_limit, RLIMIT_AS, offsetof($1, exec_context.rlimit)
$1.LimitNPROC, config_parse_limit, RLIMIT_NPROC, offsetof($1, exec_context.rlimit)
-$1.LimitMEMLOCK, config_parse_limit, RLIMIT_MEMLOCK, offsetof($1, exec_context.rlimit)
+$1.LimitMEMLOCK, config_parse_bytes_limit, RLIMIT_MEMLOCK, offsetof($1, exec_context.rlimit)
$1.LimitLOCKS, config_parse_limit, RLIMIT_LOCKS, offsetof($1, exec_context.rlimit)
$1.LimitSIGPENDING, config_parse_limit, RLIMIT_SIGPENDING, offsetof($1, exec_context.rlimit)
-$1.LimitMSGQUEUE, config_parse_limit, RLIMIT_MSGQUEUE, offsetof($1, exec_context.rlimit)
+$1.LimitMSGQUEUE, config_parse_bytes_limit, RLIMIT_MSGQUEUE, offsetof($1, exec_context.rlimit)
$1.LimitNICE, config_parse_limit, RLIMIT_NICE, offsetof($1, exec_context.rlimit)
$1.LimitRTPRIO, config_parse_limit, RLIMIT_RTPRIO, offsetof($1, exec_context.rlimit)
-$1.LimitRTTIME, config_parse_limit, RLIMIT_RTTIME, offsetof($1, exec_context.rlimit)
+$1.LimitRTTIME, config_parse_usec_limit, RLIMIT_RTTIME, offsetof($1, exec_context.rlimit)
$1.ReadWriteDirectories, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.read_write_dirs)
$1.ReadOnlyDirectories, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.read_only_dirs)
$1.InaccessibleDirectories, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.inaccessible_dirs)
@@ -125,7 +127,7 @@ $1.BlockIODeviceWeight, config_parse_blockio_device_weight, 0,
$1.BlockIOReadBandwidth, config_parse_blockio_bandwidth, 0, offsetof($1, cgroup_context)
$1.BlockIOWriteBandwidth, config_parse_blockio_bandwidth, 0, offsetof($1, cgroup_context)
$1.TasksAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.tasks_accounting)
-$1.TasksMax, config_parse_tasks_max, 0, offsetof($1, cgroup_context)
+$1.TasksMax, config_parse_tasks_max, 0, offsetof($1, cgroup_context.tasks_max)
$1.Delegate, config_parse_bool, 0, offsetof($1, cgroup_context.delegate)
$1.NetClass, config_parse_netclass, 0, offsetof($1, cgroup_context)'
)m4_dnl
@@ -133,9 +135,7 @@ Unit.Description, config_parse_unit_string_printf, 0,
Unit.Documentation, config_parse_documentation, 0, offsetof(Unit, documentation)
Unit.SourcePath, config_parse_path, 0, offsetof(Unit, source_path)
Unit.Requires, config_parse_unit_deps, UNIT_REQUIRES, 0
-Unit.RequiresOverridable, config_parse_unit_deps, UNIT_REQUIRES_OVERRIDABLE, 0
Unit.Requisite, config_parse_unit_deps, UNIT_REQUISITE, 0
-Unit.RequisiteOverridable, config_parse_unit_deps, UNIT_REQUISITE_OVERRIDABLE, 0
Unit.Wants, config_parse_unit_deps, UNIT_WANTS, 0
Unit.BindsTo, config_parse_unit_deps, UNIT_BINDS_TO, 0
Unit.BindTo, config_parse_unit_deps, UNIT_BINDS_TO, 0
@@ -149,6 +149,8 @@ Unit.ReloadPropagatedFrom, config_parse_unit_deps, UNIT_RELOAD
Unit.PropagateReloadFrom, config_parse_unit_deps, UNIT_RELOAD_PROPAGATED_FROM, 0
Unit.PartOf, config_parse_unit_deps, UNIT_PART_OF, 0
Unit.JoinsNamespaceOf, config_parse_unit_deps, UNIT_JOINS_NAMESPACE_OF, 0
+Unit.RequiresOverridable, config_parse_obsolete_unit_deps, UNIT_REQUIRES, 0
+Unit.RequisiteOverridable, config_parse_obsolete_unit_deps, UNIT_REQUISITE, 0
Unit.RequiresMountsFor, config_parse_unit_requires_mounts_for, 0, 0
Unit.StopWhenUnneeded, config_parse_bool, 0, offsetof(Unit, stop_when_unneeded)
Unit.RefuseManualStart, config_parse_bool, 0, offsetof(Unit, refuse_manual_start)
@@ -158,7 +160,7 @@ Unit.DefaultDependencies, config_parse_bool, 0,
Unit.OnFailureJobMode, config_parse_job_mode, 0, offsetof(Unit, on_failure_job_mode)
Unit.OnFailureIsolate, config_parse_job_mode_isolate, 0, offsetof(Unit, on_failure_job_mode)
Unit.IgnoreOnIsolate, config_parse_bool, 0, offsetof(Unit, ignore_on_isolate)
-Unit.IgnoreOnSnapshot, config_parse_bool, 0, offsetof(Unit, ignore_on_snapshot)
+Unit.IgnoreOnSnapshot, config_parse_warn_compat, DISABLED_LEGACY, 0
Unit.JobTimeoutSec, config_parse_sec, 0, offsetof(Unit, job_timeout)
Unit.JobTimeoutAction, config_parse_failure_action, 0, offsetof(Unit, job_timeout_action)
Unit.JobTimeoutRebootArgument, config_parse_string, 0, offsetof(Unit, job_timeout_reboot_arg)
@@ -248,6 +250,7 @@ Socket.ListenNetlink, config_parse_socket_listen, SOCKET_SOCK
Socket.ListenSpecial, config_parse_socket_listen, SOCKET_SPECIAL, 0
Socket.ListenMessageQueue, config_parse_socket_listen, SOCKET_MQUEUE, 0
Socket.ListenUSBFunction, config_parse_socket_listen, SOCKET_USB_FUNCTION, 0
+Socket.SocketProtocol, config_parse_socket_protocol, 0, 0
Socket.BindIPv6Only, config_parse_socket_bind, 0, 0,
Socket.Backlog, config_parse_unsigned, 0, offsetof(Socket, backlog)
Socket.BindToDevice, config_parse_socket_bindtodevice, 0, 0
@@ -261,6 +264,7 @@ Socket.SocketGroup, config_parse_unit_string_printf, 0,
Socket.SocketMode, config_parse_mode, 0, offsetof(Socket, socket_mode)
Socket.DirectoryMode, config_parse_mode, 0, offsetof(Socket, directory_mode)
Socket.Accept, config_parse_bool, 0, offsetof(Socket, accept)
+Socket.Writable, config_parse_bool, 0, offsetof(Socket, writable)
Socket.MaxConnections, config_parse_unsigned, 0, offsetof(Socket, max_connections)
Socket.KeepAlive, config_parse_bool, 0, offsetof(Socket, keep_alive)
Socket.KeepAliveTimeSec, config_parse_sec, 0, offsetof(Socket, keep_alive_time)
@@ -286,6 +290,7 @@ Socket.MessageQueueMaxMessages, config_parse_long, 0,
Socket.MessageQueueMessageSize, config_parse_long, 0, offsetof(Socket, mq_msgsize)
Socket.RemoveOnStop, config_parse_bool, 0, offsetof(Socket, remove_on_stop)
Socket.Symlinks, config_parse_unit_path_strv_printf, 0, offsetof(Socket, symlinks)
+Socket.FileDescriptorName, config_parse_fdname, 0, 0
Socket.Service, config_parse_socket_service, 0, 0
m4_ifdef(`HAVE_SMACK',
`Socket.SmackLabel, config_parse_string, 0, offsetof(Socket, smack)
@@ -341,7 +346,9 @@ Timer.OnUnitActiveSec, config_parse_timer, 0,
Timer.OnUnitInactiveSec, config_parse_timer, 0, 0
Timer.Persistent, config_parse_bool, 0, offsetof(Timer, persistent)
Timer.WakeSystem, config_parse_bool, 0, offsetof(Timer, wake_system)
+Timer.RemainAfterElapse, config_parse_bool, 0, offsetof(Timer, remain_after_elapse)
Timer.AccuracySec, config_parse_sec, 0, offsetof(Timer, accuracy_usec)
+Timer.RandomizedDelaySec, config_parse_sec, 0, offsetof(Timer, random_usec)
Timer.Unit, config_parse_trigger_unit, 0, 0
m4_dnl
Path.PathExists, config_parse_path_spec, 0, 0
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index f42bee4fa9..d3880b4e3c 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -20,44 +20,53 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <linux/oom.h>
#include <errno.h>
-#include <string.h>
#include <fcntl.h>
-#include <sched.h>
#include <linux/fs.h>
-#include <sys/stat.h>
-#include <sys/resource.h>
-
+#include <linux/oom.h>
#ifdef HAVE_SECCOMP
#include <seccomp.h>
#endif
+#include <sched.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
-#include "unit.h"
-#include "strv.h"
+#include "af-list.h"
+#include "alloc-util.h"
+#include "bus-error.h"
+#include "bus-internal.h"
+#include "bus-util.h"
+#include "cap-list.h"
+#include "capability-util.h"
+#include "cgroup.h"
#include "conf-parser.h"
+#include "cpu-set-util.h"
+#include "env-util.h"
+#include "errno-list.h"
+#include "escape.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "ioprio.h"
#include "load-fragment.h"
#include "log.h"
-#include "ioprio.h"
-#include "securebits.h"
#include "missing.h"
-#include "unit-name.h"
-#include "unit-printf.h"
-#include "utf8.h"
+#include "parse-util.h"
#include "path-util.h"
-#include "env-util.h"
-#include "cgroup.h"
-#include "bus-util.h"
-#include "bus-error.h"
-#include "errno-list.h"
-#include "af-list.h"
-#include "cap-list.h"
-#include "signal-util.h"
-#include "bus-internal.h"
-
+#include "process-util.h"
#ifdef HAVE_SECCOMP
#include "seccomp-util.h"
#endif
+#include "securebits.h"
+#include "signal-util.h"
+#include "stat-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "unit-name.h"
+#include "unit-printf.h"
+#include "unit.h"
+#include "utf8.h"
+#include "web-util.h"
int config_parse_warn_compat(
const char *unit,
@@ -74,15 +83,15 @@ int config_parse_warn_compat(
switch(reason) {
case DISABLED_CONFIGURATION:
- log_syntax(unit, LOG_DEBUG, filename, line, EINVAL,
+ log_syntax(unit, LOG_DEBUG, filename, line, 0,
"Support for option %s= has been disabled at compile time and it is ignored", lvalue);
break;
case DISABLED_LEGACY:
- log_syntax(unit, LOG_INFO, filename, line, EINVAL,
+ log_syntax(unit, LOG_INFO, filename, line, 0,
"Support for option %s= has been removed and it is ignored", lvalue);
break;
case DISABLED_EXPERIMENTAL:
- log_syntax(unit, LOG_INFO, filename, line, EINVAL,
+ log_syntax(unit, LOG_INFO, filename, line, 0,
"Support for option %s= has not yet been enabled and it is ignored", lvalue);
break;
};
@@ -90,52 +99,73 @@ int config_parse_warn_compat(
return 0;
}
-int config_parse_unit_deps(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_unit_deps(
+ 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) {
UnitDependency d = ltype;
Unit *u = userdata;
- const char *word, *state;
- size_t l;
+ const char *p;
assert(filename);
assert(lvalue);
assert(rvalue);
- FOREACH_WORD_QUOTED(word, l, rvalue, state) {
- _cleanup_free_ char *t = NULL, *k = NULL;
+ p = rvalue;
+ for(;;) {
+ _cleanup_free_ char *word = NULL, *k = NULL;
int r;
- t = strndup(word, l);
- if (!t)
+ r = extract_first_word(&p, &word, NULL, EXTRACT_RETAIN_ESCAPE);
+ if (r == 0)
+ break;
+ if (r == -ENOMEM)
return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
+ break;
+ }
- r = unit_name_printf(u, t, &k);
+ r = unit_name_printf(u, word, &k);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to resolve specifiers, ignoring: %s", strerror(-r));
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
continue;
}
r = unit_add_dependency_by_name(u, d, k, NULL, true);
if (r < 0)
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to add dependency on %s, ignoring: %s", k, strerror(-r));
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
}
- if (!isempty(state))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid syntax, ignoring.");
return 0;
}
+int config_parse_obsolete_unit_deps(
+ 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) {
+
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Unit dependency type %s= is obsolete, replacing by %s=, please update your unit file", lvalue, unit_dependency_to_string(ltype));
+
+ return config_parse_unit_deps(unit, filename, line, section, section_line, lvalue, ltype, rvalue, data, userdata);
+}
+
int config_parse_unit_string_printf(
const char *unit,
const char *filename,
@@ -166,16 +196,17 @@ int config_parse_unit_string_printf(
return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
}
-int config_parse_unit_strv_printf(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_unit_strv_printf(
+ 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) {
Unit *u = userdata;
_cleanup_free_ char *k = NULL;
@@ -187,24 +218,25 @@ int config_parse_unit_strv_printf(const char *unit,
assert(u);
r = unit_full_printf(u, rvalue, &k);
- if (r < 0)
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue);
+ return 0;
+ }
- return config_parse_strv(unit, filename, line, section, section_line, lvalue, ltype,
- k ? k : rvalue, data, userdata);
+ return config_parse_strv(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
}
-int config_parse_unit_path_printf(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_unit_path_printf(
+ 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) {
_cleanup_free_ char *k = NULL;
Unit *u = userdata;
@@ -217,7 +249,7 @@ int config_parse_unit_path_printf(const char *unit,
r = unit_full_printf(u, rvalue, &k);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue);
return 0;
}
@@ -256,17 +288,17 @@ int config_parse_unit_path_strv_printf(
r = unit_full_printf(u, t, &k);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve unit specifiers on %s, ignoring: %s", t, strerror(-r));
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", t);
return 0;
}
if (!utf8_is_valid(k)) {
- log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
+ log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
return 0;
}
if (!path_is_absolute(k)) {
- log_syntax(unit, LOG_ERR, filename, line, -r, "Symlink path %s is not absolute, ignoring: %s", k, strerror(-r));
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Symlink path %s is not absolute, ignoring: %m", k);
return 0;
}
@@ -279,7 +311,7 @@ int config_parse_unit_path_strv_printf(
k = NULL;
}
if (!isempty(state))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid syntax, ignoring.");
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid syntax, ignoring.");
return 0;
}
@@ -322,12 +354,8 @@ int config_parse_socket_listen(const char *unit,
p->type = ltype;
r = unit_full_printf(UNIT(s), rvalue, &p->path);
if (r < 0) {
- p->path = strdup(rvalue);
- if (!p->path)
- return log_oom();
- else
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue);
+ return 0;
}
path_kill_slashes(p->path);
@@ -337,14 +365,14 @@ int config_parse_socket_listen(const char *unit,
p->type = SOCKET_SOCKET;
r = unit_full_printf(UNIT(s), rvalue, &k);
- if (r < 0)
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue);
+ return 0;
+ }
- r = socket_address_parse_netlink(&p->address, k ?: rvalue);
+ r = socket_address_parse_netlink(&p->address, k);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to parse address value, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address value, ignoring: %s", rvalue);
return 0;
}
@@ -353,14 +381,14 @@ int config_parse_socket_listen(const char *unit,
p->type = SOCKET_SOCKET;
r = unit_full_printf(UNIT(s), rvalue, &k);
- if (r < 0)
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,"Failed to resolve unit specifiers on %s, ignoring: %m", rvalue);
+ return 0;
+ }
- r = socket_address_parse_and_warn(&p->address, k ? k : rvalue);
+ r = socket_address_parse_and_warn(&p->address, k);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to parse address value, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address value, ignoring: %s", rvalue);
return 0;
}
@@ -374,8 +402,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, EOPNOTSUPP,
- "Address family not supported, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Address family not supported, ignoring: %s", rvalue);
return 0;
}
}
@@ -395,6 +422,37 @@ int config_parse_socket_listen(const char *unit,
return 0;
}
+int config_parse_socket_protocol(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) {
+ Socket *s;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ s = SOCKET(data);
+
+ if (streq(rvalue, "udplite"))
+ s->socket_protocol = IPPROTO_UDPLITE;
+ else if (streq(rvalue, "sctp"))
+ s->socket_protocol = IPPROTO_SCTP;
+ else {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Socket protocol not supported, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ return 0;
+}
+
int config_parse_socket_bind(const char *unit,
const char *filename,
unsigned line,
@@ -422,8 +480,7 @@ int config_parse_socket_bind(const char *unit,
r = parse_boolean(rvalue);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to parse bind IPv6 only value, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse bind IPv6 only value, ignoring: %s", rvalue);
return 0;
}
@@ -455,14 +512,12 @@ int config_parse_exec_nice(const char *unit,
r = safe_atoi(rvalue, &priority);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to parse nice priority, ignoring: %s. ", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse nice priority, ignoring: %s", rvalue);
return 0;
}
if (priority < PRIO_MIN || priority >= PRIO_MAX) {
- log_syntax(unit, LOG_ERR, filename, line, ERANGE,
- "Nice priority out of range, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Nice priority out of range, ignoring: %s", rvalue);
return 0;
}
@@ -493,14 +548,12 @@ int config_parse_exec_oom_score_adjust(const char* unit,
r = safe_atoi(rvalue, &oa);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to parse the OOM score adjust value, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue);
return 0;
}
if (oa < OOM_SCORE_ADJ_MIN || oa > OOM_SCORE_ADJ_MAX) {
- log_syntax(unit, LOG_ERR, filename, line, ERANGE,
- "OOM score adjust value out of range, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "OOM score adjust value out of range, ignoring: %s", rvalue);
return 0;
}
@@ -533,9 +586,7 @@ int config_parse_exec(
assert(e);
e += ltype;
-
rvalue += strspn(rvalue, WHITESPACE);
- p = rvalue;
if (isempty(rvalue)) {
/* An empty assignment resets the list */
@@ -543,14 +594,15 @@ int config_parse_exec(
return 0;
}
+ p = rvalue;
do {
- int i;
+ _cleanup_free_ char *path = NULL, *firstword = NULL;
+ bool separate_argv0 = false, ignore = false;
+ _cleanup_free_ ExecCommand *nce = NULL;
_cleanup_strv_free_ char **n = NULL;
size_t nlen = 0, nbufsize = 0;
- _cleanup_free_ ExecCommand *nce = NULL;
- _cleanup_free_ char *path = NULL, *firstword = NULL;
char *f;
- bool separate_argv0 = false, ignore = false;
+ int i;
semicolon = false;
@@ -574,24 +626,19 @@ int config_parse_exec(
if (isempty(f)) {
/* First word is either "-" or "@" with no command. */
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Empty path in command line, ignoring: \"%s\"", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Empty path in command line, ignoring: \"%s\"", rvalue);
return 0;
}
-
if (!string_is_safe(f)) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Executable path contains special characters, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Executable path contains special characters, ignoring: %s", rvalue);
return 0;
}
if (!path_is_absolute(f)) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Executable path is not absolute, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Executable path is not absolute, ignoring: %s", rvalue);
return 0;
}
if (endswith(f, "/")) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Executable path specifies a directory, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Executable path specifies a directory, ignoring: %s", rvalue);
return 0;
}
@@ -658,8 +705,7 @@ int config_parse_exec(
}
if (!n || !n[0]) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Empty executable name or zeroeth argument, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Empty executable name or zeroeth argument, ignoring: %s", rvalue);
return 0;
}
@@ -743,8 +789,7 @@ int config_parse_exec_io_class(const char *unit,
x = ioprio_class_from_string(rvalue);
if (x < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to parse IO scheduling class, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IO scheduling class, ignoring: %s", rvalue);
return 0;
}
@@ -775,8 +820,7 @@ int config_parse_exec_io_priority(const char *unit,
r = safe_atoi(rvalue, &i);
if (r < 0 || i < 0 || i >= IOPRIO_BE_NR) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to parse IO priority, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IO priority, ignoring: %s", rvalue);
return 0;
}
@@ -808,8 +852,7 @@ int config_parse_exec_cpu_sched_policy(const char *unit,
x = sched_policy_from_string(rvalue);
if (x < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -x,
- "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
return 0;
}
@@ -842,8 +885,7 @@ int config_parse_exec_cpu_sched_prio(const char *unit,
r = safe_atoi(rvalue, &i);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
return 0;
}
@@ -852,8 +894,7 @@ int config_parse_exec_cpu_sched_prio(const char *unit,
max = sched_get_priority_max(c->cpu_sched_policy);
if (i < min || i > max) {
- log_syntax(unit, LOG_ERR, filename, line, ERANGE,
- "CPU scheduling priority is out of range, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "CPU scheduling priority is out of range, ignoring: %s", rvalue);
return 0;
}
@@ -875,50 +916,29 @@ int config_parse_exec_cpu_affinity(const char *unit,
void *userdata) {
ExecContext *c = data;
- const char *word, *state;
- size_t l;
+ _cleanup_cpu_free_ cpu_set_t *cpuset = NULL;
+ int ncpus;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- if (isempty(rvalue)) {
- /* An empty assignment resets the CPU list */
- if (c->cpuset)
- CPU_FREE(c->cpuset);
- c->cpuset = NULL;
- return 0;
- }
-
- FOREACH_WORD_QUOTED(word, l, rvalue, state) {
- _cleanup_free_ char *t = NULL;
- int r;
- unsigned cpu;
-
- t = strndup(word, l);
- if (!t)
- return log_oom();
-
- r = safe_atou(t, &cpu);
-
- if (!c->cpuset) {
- c->cpuset = cpu_set_malloc(&c->cpuset_ncpus);
- if (!c->cpuset)
- return log_oom();
- }
+ ncpus = parse_cpu_set_and_warn(rvalue, &cpuset, unit, filename, line, lvalue);
+ if (ncpus < 0)
+ return ncpus;
- if (r < 0 || cpu >= c->cpuset_ncpus) {
- log_syntax(unit, LOG_ERR, filename, line, ERANGE,
- "Failed to parse CPU affinity '%s', ignoring: %s", t, rvalue);
- return 0;
- }
+ if (c->cpuset)
+ CPU_FREE(c->cpuset);
- CPU_SET_S(cpu, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset);
+ if (ncpus == 0)
+ /* An empty assignment resets the CPU list */
+ c->cpuset = NULL;
+ else {
+ c->cpuset = cpuset;
+ cpuset = NULL;
}
- if (!isempty(state))
- log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
- "Trailing garbage, ignoring.");
+ c->cpuset_ncpus = ncpus;
return 0;
}
@@ -944,8 +964,7 @@ int config_parse_exec_capabilities(const char *unit,
cap = cap_from_text(rvalue);
if (!cap) {
- log_syntax(unit, LOG_ERR, filename, line, errno,
- "Failed to parse capabilities, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, errno, "Failed to parse capabilities, ignoring: %s", rvalue);
return 0;
}
@@ -996,34 +1015,32 @@ int config_parse_exec_secure_bits(const char *unit,
else if (first_word(word, "noroot-locked"))
c->secure_bits |= 1<<SECURE_NOROOT_LOCKED;
else {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to parse secure bits, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse secure bits, ignoring: %s", rvalue);
return 0;
}
}
if (!isempty(state))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Invalid syntax, garbage at the end, ignoring.");
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid syntax, garbage at the end, ignoring.");
return 0;
}
-int config_parse_bounding_set(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_capability_set(
+ 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) {
- uint64_t *capability_bounding_set_drop = data;
- const char *word, *state;
- size_t l;
+ uint64_t *capability_set = data;
+ uint64_t sum = 0, initial = 0;
bool invert = false;
- uint64_t sum = 0;
+ const char *p;
assert(filename);
assert(lvalue);
@@ -1035,83 +1052,256 @@ int config_parse_bounding_set(const char *unit,
rvalue++;
}
- /* Note that we store this inverted internally, since the
- * kernel wants it like this. But we actually expose it
- * non-inverted everywhere to have a fully normalized
- * interface. */
+ if (strcmp(lvalue, "CapabilityBoundingSet") == 0)
+ initial = CAP_ALL; /* initialized to all bits on */
+ /* else "AmbientCapabilities" initialized to all bits off */
- FOREACH_WORD_QUOTED(word, l, rvalue, state) {
- _cleanup_free_ char *t = NULL;
- int cap;
+ p = rvalue;
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
+ int cap, r;
- t = strndup(word, l);
- if (!t)
+ r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
+ if (r == 0)
+ break;
+ if (r == -ENOMEM)
return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse word, ignoring: %s", rvalue);
+ break;
+ }
- cap = capability_from_name(t);
+ cap = capability_from_name(word);
if (cap < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse capability in bounding set, ignoring: %s", t);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse capability in bounding/ambient set, ignoring: %s", word);
continue;
}
- sum |= ((uint64_t) 1ULL) << (uint64_t) cap;
+ sum |= ((uint64_t) UINT64_C(1)) << (uint64_t) cap;
}
- if (!isempty(state))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Trailing garbage, ignoring.");
- if (invert)
- *capability_bounding_set_drop |= sum;
+ sum = invert ? ~sum : sum;
+
+ if (sum == 0 || *capability_set == initial)
+ /* "" or uninitialized data -> replace */
+ *capability_set = sum;
else
- *capability_bounding_set_drop |= ~sum;
+ /* previous data -> merge */
+ *capability_set |= sum;
return 0;
}
-int config_parse_limit(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) {
+static int rlim_parse_u64(const char *val, rlim_t *res) {
+ int r = 0;
- struct rlimit **rl = data;
- unsigned long long u;
+ if (streq(val, "infinity"))
+ *res = RLIM_INFINITY;
+ else {
+ uint64_t u;
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
+ /* setrlimit(2) suggests rlim_t is always 64bit on Linux. */
+ assert_cc(sizeof(rlim_t) == sizeof(uint64_t));
- rl += ltype;
+ r = safe_atou64(val, &u);
+ if (r >= 0 && u >= (uint64_t) RLIM_INFINITY)
+ r = -ERANGE;
+ if (r == 0)
+ *res = (rlim_t) u;
+ }
+ return r;
+}
+
+static int rlim_parse_size(const char *val, rlim_t *res) {
+ int r = 0;
- if (streq(rvalue, "infinity"))
- u = (unsigned long long) RLIM_INFINITY;
+ if (streq(val, "infinity"))
+ *res = RLIM_INFINITY;
else {
- int r;
+ uint64_t u;
- r = safe_atollu(rvalue, &u);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to parse resource value, ignoring: %s", rvalue);
- return 0;
- }
+ r = parse_size(val, 1024, &u);
+ if (r >= 0 && u >= (uint64_t) RLIM_INFINITY)
+ r = -ERANGE;
+ if (r == 0)
+ *res = (rlim_t) u;
+ }
+ return r;
+}
+
+static int rlim_parse_sec(const char *val, rlim_t *res) {
+ int r = 0;
+
+ if (streq(val, "infinity"))
+ *res = RLIM_INFINITY;
+ else {
+ usec_t t;
+
+ r = parse_sec(val, &t);
+ if (r < 0)
+ return r;
+ if (t == USEC_INFINITY)
+ *res = RLIM_INFINITY;
+ else
+ *res = (rlim_t) (DIV_ROUND_UP(t, USEC_PER_SEC));
+
+ }
+ return r;
+}
+
+static int rlim_parse_usec(const char *val, rlim_t *res) {
+ int r = 0;
+
+ if (streq(val, "infinity"))
+ *res = RLIM_INFINITY;
+ else {
+ usec_t t;
+
+ r = parse_time(val, &t, 1);
+ if (r < 0)
+ return r;
+ if (t == USEC_INFINITY)
+ *res = RLIM_INFINITY;
+ else
+ *res = (rlim_t) t;
}
+ return r;
+}
+
+static int parse_rlimit_range(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *value,
+ struct rlimit **rl,
+ int (*rlim_parser)(const char *, rlim_t *)) {
+
+ const char *whole_value = value;
+ rlim_t soft, hard;
+ _cleanup_free_ char *sword = NULL, *hword = NULL;
+ int nwords, r;
+
+ assert(value);
+
+ /* <value> or <soft:hard> */
+ nwords = extract_many_words(&value, ":", EXTRACT_DONT_COALESCE_SEPARATORS, &sword, &hword, NULL);
+ r = nwords < 0 ? nwords : nwords == 0 ? -EINVAL : 0;
+
+ if (r == 0)
+ r = rlim_parser(sword, &soft);
+ if (r == 0 && nwords == 2)
+ r = rlim_parser(hword, &hard);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", whole_value);
+ return 0;
+ }
+ if (nwords == 2 && soft > hard)
+ return log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid resource value ("RLIM_FMT" > "RLIM_FMT"), ignoring: %s", soft, hard, whole_value);
if (!*rl) {
*rl = new(struct rlimit, 1);
if (!*rl)
return log_oom();
}
-
- (*rl)->rlim_cur = (*rl)->rlim_max = (rlim_t) u;
+ (*rl)->rlim_cur = soft;
+ (*rl)->rlim_max = nwords == 2 ? hard : soft;
return 0;
}
+int config_parse_limit(
+ 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) {
+
+ struct rlimit **rl = data;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ rl += ltype;
+ return parse_rlimit_range(unit, filename, line, rvalue, rl, rlim_parse_u64);
+}
+
+int config_parse_bytes_limit(
+ 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) {
+
+ struct rlimit **rl = data;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ rl += ltype;
+ return parse_rlimit_range(unit, filename, line, rvalue, rl, rlim_parse_size);
+}
+
+int config_parse_sec_limit(
+ 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) {
+
+ struct rlimit **rl = data;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ rl += ltype;
+ return parse_rlimit_range(unit, filename, line, rvalue, rl, rlim_parse_sec);
+}
+
+int config_parse_usec_limit(
+ 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) {
+
+ struct rlimit **rl = data;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ rl += ltype;
+ return parse_rlimit_range(unit, filename, line, rvalue, rl, rlim_parse_usec);
+}
+
+
+
#ifdef HAVE_SYSV_COMPAT
int config_parse_sysv_priority(const char *unit,
const char *filename,
@@ -1134,8 +1324,7 @@ int config_parse_sysv_priority(const char *unit,
r = safe_atoi(rvalue, &i);
if (r < 0 || i < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to parse SysV start priority, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse SysV start priority, ignoring: %s", rvalue);
return 0;
}
@@ -1158,38 +1347,28 @@ int config_parse_exec_mount_flags(const char *unit,
void *data,
void *userdata) {
- ExecContext *c = data;
- const char *word, *state;
- size_t l;
+
unsigned long flags = 0;
+ ExecContext *c = data;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- FOREACH_WORD_SEPARATOR(word, l, rvalue, ", ", state) {
- _cleanup_free_ char *t;
-
- t = strndup(word, l);
- if (!t)
- return log_oom();
-
- if (streq(t, "shared"))
- flags = MS_SHARED;
- else if (streq(t, "slave"))
- flags = MS_SLAVE;
- 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);
- return 0;
- }
+ if (streq(rvalue, "shared"))
+ flags = MS_SHARED;
+ else if (streq(rvalue, "slave"))
+ flags = MS_SLAVE;
+ else if (streq(rvalue, "private"))
+ flags = MS_PRIVATE;
+ else {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse mount flag %s, ignoring.", rvalue);
+ return 0;
}
- if (!isempty(state))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Trailing garbage, ignoring.");
c->mount_flags = flags;
+
return 0;
}
@@ -1230,8 +1409,7 @@ int config_parse_exec_selinux_context(
r = unit_name_printf(u, rvalue, &k);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to resolve specifiers, ignoring: %s", strerror(-r));
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
return 0;
}
@@ -1279,8 +1457,7 @@ int config_parse_exec_apparmor_profile(
r = unit_name_printf(u, rvalue, &k);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to resolve specifiers, ignoring: %s", strerror(-r));
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
return 0;
}
@@ -1328,8 +1505,7 @@ int config_parse_exec_smack_process_label(
r = unit_name_printf(u, rvalue, &k);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to resolve specifiers, ignoring: %s", strerror(-r));
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
return 0;
}
@@ -1370,23 +1546,18 @@ int config_parse_timer(const char *unit,
b = timer_base_from_string(lvalue);
if (b < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -b,
- "Failed to parse timer base, ignoring: %s", lvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse timer base, ignoring: %s", lvalue);
return 0;
}
if (b == TIMER_CALENDAR) {
if (calendar_spec_from_string(rvalue, &c) < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to parse calendar specification, ignoring: %s",
- rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse calendar specification, ignoring: %s", rvalue);
return 0;
}
} else {
if (parse_sec(rvalue, &u) < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to parse timer value, ignoring: %s",
- rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse timer value, ignoring: %s", rvalue);
return 0;
}
}
@@ -1429,33 +1600,30 @@ int config_parse_trigger_unit(
assert(data);
if (!set_isempty(u->dependencies[UNIT_TRIGGERS])) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Multiple units to trigger specified, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Multiple units to trigger specified, ignoring: %s", rvalue);
return 0;
}
r = unit_name_printf(u, rvalue, &p);
- if (r < 0)
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to resolve specifiers, ignoring: %s", strerror(-r));
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
+ return 0;
+ }
- type = unit_name_to_type(p ?: rvalue);
+ type = unit_name_to_type(p);
if (type < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Unit type not valid, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Unit type not valid, ignoring: %s", rvalue);
return 0;
}
if (type == u->type) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Trigger cannot be of same type, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Trigger cannot be of same type, ignoring: %s", rvalue);
return 0;
}
- r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p ?: rvalue, NULL, true);
+ r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p, NULL, true);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to add trigger on %s, ignoring: %s", p ?: rvalue, strerror(-r));
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add trigger on %s, ignoring: %m", p);
return 0;
}
@@ -1492,25 +1660,18 @@ int config_parse_path_spec(const char *unit,
b = path_type_from_string(lvalue);
if (b < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to parse path type, ignoring: %s", lvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse path type, ignoring: %s", lvalue);
return 0;
}
r = unit_full_printf(UNIT(p), rvalue, &k);
if (r < 0) {
- k = strdup(rvalue);
- if (!k)
- return log_oom();
- else
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to resolve unit specifiers on %s. Ignoring.",
- rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
+ return 0;
}
if (!path_is_absolute(k)) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Path is not absolute, ignoring: %s", k);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Path is not absolute, ignoring: %s", k);
return 0;
}
@@ -1541,11 +1702,11 @@ int config_parse_socket_service(
void *data,
void *userdata) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_free_ char *p = NULL;
Socket *s = data;
- int r;
Unit *x;
- _cleanup_free_ char *p = NULL;
+ int r;
assert(filename);
assert(lvalue);
@@ -1559,13 +1720,13 @@ int config_parse_socket_service(
}
if (!endswith(p, ".service")) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Unit must be of type service, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Unit must be of type service, ignoring: %s", rvalue);
return 0;
}
r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
return 0;
}
@@ -1574,6 +1735,50 @@ int config_parse_socket_service(
return 0;
}
+int config_parse_fdname(
+ 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) {
+
+ _cleanup_free_ char *p = NULL;
+ Socket *s = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (isempty(rvalue)) {
+ s->fdname = mfree(s->fdname);
+ return 0;
+ }
+
+ r = unit_name_printf(UNIT(s), rvalue, &p);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ if (!fdname_is_valid(p)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid file descriptor name, ignoring: %s", p);
+ return 0;
+ }
+
+ free(s->fdname);
+ s->fdname = p;
+ p = NULL;
+
+ return 0;
+}
+
int config_parse_service_sockets(
const char *unit,
const char *filename,
@@ -1587,8 +1792,7 @@ int config_parse_service_sockets(
void *userdata) {
Service *s = data;
- const char *word, *state;
- size_t l;
+ const char *p;
int r;
assert(filename);
@@ -1596,21 +1800,28 @@ int config_parse_service_sockets(
assert(rvalue);
assert(data);
- FOREACH_WORD_QUOTED(word, l, rvalue, state) {
- _cleanup_free_ char *t = NULL, *k = NULL;
+ p = rvalue;
+ for(;;) {
+ _cleanup_free_ char *word = NULL, *k = NULL;
- t = strndup(word, l);
- if (!t)
+ r = extract_first_word(&p, &word, NULL, 0);
+ if (r == 0)
+ break;
+ if (r == -ENOMEM)
return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Trailing garbage in sockets, ignoring: %s", rvalue);
+ break;
+ }
- r = unit_name_printf(UNIT(s), t, &k);
+ r = unit_name_printf(UNIT(s), word, &k);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
continue;
}
if (!endswith(k, ".socket")) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Unit must be of type socket, ignoring: %s", k);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Unit must be of type socket, ignoring: %s", k);
continue;
}
@@ -1622,8 +1833,6 @@ int config_parse_service_sockets(
if (r < 0)
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
}
- if (!isempty(state))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Trailing garbage, ignoring.");
return 0;
}
@@ -1656,7 +1865,7 @@ int config_parse_bus_name(
}
if (!service_name_is_valid(k)) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Invalid bus name %s, ignoring.", k);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid bus name %s, ignoring.", k);
return 0;
}
@@ -1708,7 +1917,7 @@ int config_parse_busname_service(
void *data,
void *userdata) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
BusName *n = data;
int r;
Unit *x;
@@ -1721,21 +1930,18 @@ int config_parse_busname_service(
r = unit_name_printf(UNIT(n), rvalue, &p);
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);
return 0;
}
if (!endswith(p, ".service")) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Unit must be of type service, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Unit must be of type service, ignoring: %s", rvalue);
return 0;
}
r = manager_load_unit(UNIT(n)->manager, p, NULL, &error, &x);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
return 0;
}
@@ -1785,8 +1991,7 @@ int config_parse_bus_policy(
access_str = strpbrk(id_str, WHITESPACE);
if (!access_str) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Invalid busname policy value '%s'", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid busname policy value '%s'", rvalue);
return 0;
}
@@ -1796,8 +2001,7 @@ int config_parse_bus_policy(
p->access = bus_policy_access_from_string(access_str);
if (p->access < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Invalid busname policy access type '%s'", access_str);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid busname policy access type '%s'", access_str);
return 0;
}
@@ -1839,8 +2043,7 @@ int config_parse_bus_endpoint_policy(
access_str = strpbrk(name, WHITESPACE);
if (!access_str) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Invalid endpoint policy value '%s'", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid endpoint policy value '%s'", rvalue);
return 0;
}
@@ -1851,21 +2054,83 @@ int config_parse_bus_endpoint_policy(
access = bus_policy_access_from_string(access_str);
if (access <= _BUS_POLICY_ACCESS_INVALID ||
access >= _BUS_POLICY_ACCESS_MAX) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Invalid endpoint policy access type '%s'", access_str);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid endpoint policy access type '%s'", access_str);
return 0;
}
if (!c->bus_endpoint) {
r = bus_endpoint_new(&c->bus_endpoint);
-
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to create bus endpoint object: %m");
}
return bus_endpoint_add_policy(c->bus_endpoint, name, access);
}
+int config_parse_working_directory(
+ 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) {
+
+ ExecContext *c = data;
+ Unit *u = userdata;
+ bool missing_ok;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(c);
+ assert(u);
+
+ if (rvalue[0] == '-') {
+ missing_ok = true;
+ rvalue++;
+ } else
+ missing_ok = false;
+
+ if (streq(rvalue, "~")) {
+ c->working_directory_home = true;
+ c->working_directory = mfree(c->working_directory);
+ } else {
+ _cleanup_free_ char *k = NULL;
+
+ r = unit_full_printf(u, rvalue, &k);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in working directory path '%s', ignoring: %m", rvalue);
+ return 0;
+ }
+
+ path_kill_slashes(k);
+
+ if (!utf8_is_valid(k)) {
+ log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
+ return 0;
+ }
+
+ if (!path_is_absolute(k)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Working directory path '%s' is not absolute, ignoring.", rvalue);
+ return 0;
+ }
+
+ free(c->working_directory);
+ c->working_directory = k;
+ k = NULL;
+
+ c->working_directory_home = false;
+ }
+
+ c->working_directory_missing_ok = missing_ok;
+ return 0;
+}
+
int config_parse_unit_env_file(const char *unit,
const char *filename,
unsigned line,
@@ -1880,7 +2145,6 @@ int config_parse_unit_env_file(const char *unit,
char ***env = data;
Unit *u = userdata;
_cleanup_free_ char *n = NULL;
- const char *s;
int r;
assert(filename);
@@ -1895,18 +2159,17 @@ int config_parse_unit_env_file(const char *unit,
}
r = unit_full_printf(u, rvalue, &n);
- if (r < 0)
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to resolve specifiers, ignoring: %s", rvalue);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
+ return 0;
+ }
- s = n ?: rvalue;
- if (!path_is_absolute(s[0] == '-' ? s + 1 : s)) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Path '%s' is not absolute, ignoring.", s);
+ if (!path_is_absolute(n[0] == '-' ? n + 1 : n)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Path '%s' is not absolute, ignoring.", n);
return 0;
}
- r = strv_extend(env, s);
+ r = strv_extend(env, n);
if (r < 0)
return log_oom();
@@ -1944,14 +2207,17 @@ 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);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
+ return 0;
+ }
}
- if (!k)
+ if (!k) {
k = strdup(rvalue);
- if (!k)
- return log_oom();
+ if (!k)
+ return log_oom();
+ }
FOREACH_WORD_QUOTED(word, l, k, state) {
_cleanup_free_ char *n = NULL;
@@ -1964,7 +2230,7 @@ int config_parse_environ(const char *unit,
}
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, 0, "Invalid environment assignment, ignoring: %s", rvalue);
continue;
}
@@ -1976,8 +2242,71 @@ int config_parse_environ(const char *unit,
*env = x;
}
if (!isempty(state))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Trailing garbage, ignoring.");
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
+
+ return 0;
+}
+
+int config_parse_pass_environ(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 *whole_rvalue = rvalue;
+ char*** passenv = data;
+ _cleanup_strv_free_ char **n = NULL;
+ size_t nlen = 0, nbufsize = 0;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+ *passenv = strv_free(*passenv);
+ return 0;
+ }
+
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
+
+ r = extract_first_word(&rvalue, &word, WHITESPACE, EXTRACT_QUOTES);
+ if (r == 0)
+ break;
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Trailing garbage in %s, ignoring: %s", lvalue, whole_rvalue);
+ break;
+ }
+
+ if (!env_name_is_valid(word)) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Invalid environment name for %s, ignoring: %s", lvalue, word);
+ continue;
+ }
+
+ if (!GREEDY_REALLOC(n, nbufsize, nlen + 2))
+ return log_oom();
+ n[nlen++] = word;
+ n[nlen] = NULL;
+ word = NULL;
+ }
+
+ if (n) {
+ r = strv_extend_strv(passenv, n, true);
+ if (r < 0)
+ return r;
+ }
return 0;
}
@@ -2002,8 +2331,7 @@ int config_parse_ip_tos(const char *unit,
x = ip_tos_from_string(rvalue);
if (x < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to parse IP TOS value, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IP TOS value, ignoring: %s", rvalue);
return 0;
}
@@ -2051,12 +2379,12 @@ int config_parse_unit_condition_path(
r = unit_full_printf(u, rvalue, &p);
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);
return 0;
}
if (!path_is_absolute(p)) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Path in condition not absolute, ignoring: %s", p);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Path in condition not absolute, ignoring: %s", p);
return 0;
}
@@ -2108,7 +2436,7 @@ int config_parse_unit_condition_string(
r = unit_full_printf(u, rvalue, &s);
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);
return 0;
}
@@ -2157,7 +2485,7 @@ int config_parse_unit_condition_null(
b = parse_boolean(rvalue);
if (b < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -b, "Failed to parse boolean value in condition, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, b, "Failed to parse boolean value in condition, ignoring: %s", rvalue);
return 0;
}
@@ -2205,20 +2533,18 @@ int config_parse_unit_requires_mounts_for(
return log_oom();
if (!utf8_is_valid(n)) {
- log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
+ log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
continue;
}
r = unit_require_mounts_for(u, n);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to add required mount for, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add required mount for, ignoring: %s", rvalue);
continue;
}
}
if (!isempty(state))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Trailing garbage, ignoring.");
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
return 0;
}
@@ -2259,8 +2585,7 @@ int config_parse_documentation(const char *unit,
if (documentation_url_is_valid(*a))
*(b++) = *a;
else {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Invalid URL, ignoring: %s", *a);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid URL, ignoring: %s", *a);
free(*a);
}
}
@@ -2355,8 +2680,7 @@ int config_parse_syscall_filter(
id = seccomp_syscall_resolve_name(t);
if (id < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to parse system call, ignoring: %s", t);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse system call, ignoring: %s", t);
continue;
}
@@ -2373,8 +2697,7 @@ int config_parse_syscall_filter(
set_remove(c->syscall_filter, INT_TO_PTR(id + 1));
}
if (!isempty(state))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Trailing garbage, ignoring.");
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
/* Turn on NNP, but only if it wasn't configured explicitly
* before, and only if we are in user mode. */
@@ -2420,8 +2743,7 @@ int config_parse_syscall_archs(
r = seccomp_arch_from_string(t, &a);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to parse system call architecture, ignoring: %s", t);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse system call architecture, ignoring: %s", t);
continue;
}
@@ -2432,8 +2754,7 @@ int config_parse_syscall_archs(
return log_oom();
}
if (!isempty(state))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Trailing garbage, ignoring.");
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
return 0;
}
@@ -2465,8 +2786,7 @@ int config_parse_syscall_errno(
e = errno_from_name(rvalue);
if (e < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to parse error number, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse error number, ignoring: %s", rvalue);
return 0;
}
@@ -2526,8 +2846,7 @@ int config_parse_address_families(
af = af_from_name(t);
if (af <= 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to parse address family, ignoring: %s", t);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse address family, ignoring: %s", t);
continue;
}
@@ -2544,8 +2863,7 @@ int config_parse_address_families(
set_remove(c->address_families, INT_TO_PTR(af));
}
if (!isempty(state))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Trailing garbage, ignoring.");
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
return 0;
}
@@ -2648,15 +2966,12 @@ int config_parse_cpu_quota(
}
if (!endswith(rvalue, "%")) {
-
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "CPU quota '%s' not ending in '%%'. Ignoring.", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "CPU quota '%s' not ending in '%%'. Ignoring.", rvalue);
return 0;
}
if (sscanf(rvalue, "%lf%%", &percent) != 1 || percent <= 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "CPU quota '%s' invalid. Ignoring.", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "CPU quota '%s' invalid. Ignoring.", rvalue);
return 0;
}
@@ -2688,7 +3003,7 @@ int config_parse_memory_limit(
r = parse_size(rvalue, 1024, &bytes);
if (r < 0 || bytes < 1) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Memory limit '%s' invalid. Ignoring.", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Memory limit '%s' invalid. Ignoring.", rvalue);
return 0;
}
@@ -2708,21 +3023,21 @@ int config_parse_tasks_max(
void *data,
void *userdata) {
- CGroupContext *c = data;
- uint64_t u;
+ uint64_t *tasks_max = data, u;
int r;
if (isempty(rvalue) || streq(rvalue, "infinity")) {
- c->tasks_max = (uint64_t) -1;
+ *tasks_max = (uint64_t) -1;
return 0;
}
r = safe_atou64(rvalue, &u);
if (r < 0 || u < 1) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Maximum tasks value '%s' invalid. Ignoring.", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Maximum tasks value '%s' invalid. Ignoring.", rvalue);
return 0;
}
+ *tasks_max = u;
return 0;
}
@@ -2759,8 +3074,7 @@ int config_parse_device_allow(
if (!startswith(path, "/dev/") &&
!startswith(path, "block-") &&
!startswith(path, "char-")) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Invalid device node path '%s'. Ignoring.", path);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
return 0;
}
@@ -2769,8 +3083,7 @@ int config_parse_device_allow(
m = "rwm";
if (!in_charset(m, "rwm")) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Invalid device rights '%s'. Ignoring.", m);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device rights '%s'. Ignoring.", m);
return 0;
}
@@ -2852,7 +3165,7 @@ int config_parse_blockio_device_weight(
weight += strspn(weight, WHITESPACE);
if (isempty(weight)) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Expected block device and device weight. Ignoring.");
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Expected block device and device weight. Ignoring.");
return 0;
}
@@ -2861,7 +3174,7 @@ int config_parse_blockio_device_weight(
return log_oom();
if (!path_startswith(path, "/dev")) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid device node path '%s'. Ignoring.", path);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
return 0;
}
@@ -2928,8 +3241,7 @@ int config_parse_blockio_bandwidth(
bandwidth += strspn(bandwidth, WHITESPACE);
if (!*bandwidth) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Expected space separated pair of device node and bandwidth. Ignoring.");
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
return 0;
}
@@ -2938,15 +3250,13 @@ int config_parse_blockio_bandwidth(
return log_oom();
if (!path_startswith(path, "/dev")) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Invalid device node path '%s'. Ignoring.", path);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
return 0;
}
r = parse_size(bandwidth, 1000, &bytes);
if (r < 0 || bytes <= 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue);
return 0;
}
@@ -2991,13 +3301,12 @@ int config_parse_netclass(
r = safe_atou32(rvalue, &v);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Netclass '%s' invalid. Ignoring.", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Netclass '%s' invalid. Ignoring.", rvalue);
return 0;
}
if (v > CGROUP_NETCLASS_FIXED_MAX)
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ log_syntax(unit, LOG_ERR, filename, line, 0,
"Fixed netclass %" PRIu32 " out of allowed range (0-%d). Applying anyway.", v, (uint32_t) CGROUP_NETCLASS_FIXED_MAX);
c->netclass_id = v;
@@ -3029,8 +3338,7 @@ int config_parse_job_mode_isolate(
r = parse_boolean(rvalue);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to parse boolean, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse boolean, ignoring: %s", rvalue);
return 0;
}
@@ -3076,14 +3384,12 @@ int config_parse_runtime_directory(
r = unit_name_printf(u, t, &n);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to resolve specifiers, ignoring: %s", strerror(-r));
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
continue;
}
if (!filename_is_valid(n)) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Runtime directory is not valid, ignoring assignment: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Runtime directory is not valid, ignoring assignment: %s", rvalue);
continue;
}
@@ -3094,8 +3400,7 @@ int config_parse_runtime_directory(
n = NULL;
}
if (!isempty(state))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Trailing garbage, ignoring.");
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
return 0;
}
@@ -3142,15 +3447,13 @@ int config_parse_set_status(
val = signal_from_string_try_harder(temp);
if (val <= 0) {
- log_syntax(unit, LOG_ERR, filename, line, -val,
- "Failed to parse value, ignoring: %s", word);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse value, ignoring: %s", word);
continue;
}
set = &status_set->signal;
} else {
if (val < 0 || val > 255) {
- log_syntax(unit, LOG_ERR, filename, line, ERANGE,
- "Value %d is outside range 0-255, ignoring", val);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Value %d is outside range 0-255, ignoring", val);
continue;
}
set = &status_set->status;
@@ -3162,14 +3465,12 @@ int config_parse_set_status(
r = set_put(*set, INT_TO_PTR(val));
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Unable to store: %s", word);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Unable to store: %s", word);
return r;
}
}
if (!isempty(state))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Trailing garbage, ignoring.");
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
return 0;
}
@@ -3187,8 +3488,8 @@ int config_parse_namespace_path_strv(
void *userdata) {
char*** sv = data;
- const char *word, *state;
- size_t l;
+ const char *prev;
+ const char *cur;
int r;
assert(filename);
@@ -3202,37 +3503,43 @@ int config_parse_namespace_path_strv(
return 0;
}
- FOREACH_WORD_QUOTED(word, l, rvalue, state) {
- _cleanup_free_ char *n;
+ prev = cur = rvalue;
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
int offset;
- n = strndup(word, l);
- if (!n)
+ r = extract_first_word(&cur, &word, NULL, EXTRACT_QUOTES);
+ if (r == 0)
+ break;
+ if (r == -ENOMEM)
return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Trailing garbage, ignoring: %s", prev);
+ return 0;
+ }
- if (!utf8_is_valid(n)) {
- log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
+ if (!utf8_is_valid(word)) {
+ log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, word);
+ prev = cur;
continue;
}
- offset = n[0] == '-';
- if (!path_is_absolute(n + offset)) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Not an absolute path, ignoring: %s", rvalue);
+ offset = word[0] == '-';
+ if (!path_is_absolute(word + offset)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute path, ignoring: %s", word);
+ prev = cur;
continue;
}
- path_kill_slashes(n);
+ path_kill_slashes(word + offset);
- r = strv_push(sv, n);
+ r = strv_push(sv, word);
if (r < 0)
return log_oom();
- n = NULL;
+ prev = cur;
+ word = NULL;
}
- if (!isempty(state))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Trailing garbage, ignoring.");
return 0;
}
@@ -3259,8 +3566,7 @@ int config_parse_no_new_privileges(
k = parse_boolean(rvalue);
if (k < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -k,
- "Failed to parse boolean value, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
return 0;
}
@@ -3303,8 +3609,7 @@ int config_parse_protect_home(
h = protect_home_from_string(rvalue);
if (h < 0){
- log_syntax(unit, LOG_ERR, filename, line, -h,
- "Failed to parse protect home value, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect home value, ignoring: %s", rvalue);
return 0;
}
@@ -3347,8 +3652,7 @@ int config_parse_protect_system(
s = protect_system_from_string(rvalue);
if (s < 0){
- log_syntax(unit, LOG_ERR, filename, line, -s,
- "Failed to parse protect system value, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect system value, ignoring: %s", rvalue);
return 0;
}
@@ -3701,7 +4005,7 @@ void unit_dump_config_items(FILE *f) {
{ config_parse_log_level, "LEVEL" },
{ config_parse_exec_capabilities, "CAPABILITIES" },
{ config_parse_exec_secure_bits, "SECUREBITS" },
- { config_parse_bounding_set, "BOUNDINGSET" },
+ { config_parse_capability_set, "BOUNDINGSET" },
{ config_parse_limit, "LIMIT" },
{ config_parse_unit_deps, "UNIT [...]" },
{ config_parse_exec, "PATH [ARGUMENT [...]]" },
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index 5d0a09249f..f0027a6b43 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -31,12 +31,14 @@ void unit_dump_config_items(FILE *f);
int config_parse_warn_compat(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_unit_deps(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_obsolete_unit_deps(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_unit_string_printf(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_unit_strv_printf(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_unit_path_printf(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_unit_path_strv_printf(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_documentation(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_socket_listen(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_socket_protocol(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_socket_bind(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_nice(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_oom_score_adjust(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);
@@ -54,8 +56,11 @@ int config_parse_exec_cpu_sched_prio(const char *unit, const char *filename, uns
int config_parse_exec_cpu_affinity(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_capabilities(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_secure_bits(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_bounding_set(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_capability_set(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_limit(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_bytes_limit(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_sec_limit(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_usec_limit(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_sysv_priority(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_kill_signal(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_mount_flags(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);
@@ -81,6 +86,7 @@ int config_parse_syscall_filter(const char *unit, const char *filename, unsigned
int config_parse_syscall_archs(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_syscall_errno(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_environ(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_pass_environ(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_unit_slice(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_cpu_shares(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_memory_limit(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);
@@ -106,6 +112,8 @@ int config_parse_protect_home(const char* unit, const char *filename, unsigned l
int config_parse_protect_system(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_bus_name(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_utmp_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_working_directory(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_fdname(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);
/* gperf prototypes */
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, unsigned length);
diff --git a/src/core/locale-setup.c b/src/core/locale-setup.c
index 6961c26674..4c8d920389 100644
--- a/src/core/locale-setup.c
+++ b/src/core/locale-setup.c
@@ -19,16 +19,17 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
#include <errno.h>
+#include <stdlib.h>
+#include "env-util.h"
+#include "fileio.h"
#include "locale-setup.h"
+#include "locale-util.h"
+#include "string-util.h"
+#include "strv.h"
#include "util.h"
#include "virt.h"
-#include "fileio.h"
-#include "strv.h"
-#include "env-util.h"
-#include "locale-util.h"
int locale_setup(char ***environment) {
char **add;
diff --git a/src/core/loopback-setup.c b/src/core/loopback-setup.c
index 4503fc9dcc..4b2a97fd12 100644
--- a/src/core/loopback-setup.c
+++ b/src/core/loopback-setup.c
@@ -23,12 +23,13 @@
#include <stdlib.h>
#include "sd-netlink.h"
-#include "netlink-util.h"
-#include "missing.h"
+
#include "loopback-setup.h"
+#include "missing.h"
+#include "netlink-util.h"
static int start_loopback(sd_netlink *rtnl) {
- _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
int r;
r = sd_rtnl_message_new_link(rtnl, &req, RTM_SETLINK, LOOPBACK_IFINDEX);
@@ -47,7 +48,7 @@ static int start_loopback(sd_netlink *rtnl) {
}
static bool check_loopback(sd_netlink *rtnl) {
- _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
unsigned flags;
int r;
@@ -67,7 +68,7 @@ static bool check_loopback(sd_netlink *rtnl) {
}
int loopback_setup(void) {
- _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
int r;
r = sd_netlink_open(&rtnl);
diff --git a/src/core/machine-id-setup.c b/src/core/machine-id-setup.c
index 8f682c6d10..09b0449c80 100644
--- a/src/core/machine-id-setup.c
+++ b/src/core/machine-id-setup.c
@@ -19,24 +19,34 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
-#include <stdio.h>
#include <errno.h>
-#include <string.h>
#include <fcntl.h>
+#include <sched.h>
+#include <stdio.h>
+#include <string.h>
#include <sys/mount.h>
+#include <unistd.h>
-#include "systemd/sd-id128.h"
+#include "sd-id128.h"
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "hexdecoct.h"
+#include "io-util.h"
+#include "log.h"
#include "machine-id-setup.h"
#include "macro.h"
-#include "util.h"
#include "mkdir.h"
-#include "log.h"
-#include "virt.h"
-#include "fileio.h"
+#include "mount-util.h"
#include "path-util.h"
#include "process-util.h"
+#include "stat-util.h"
+#include "string-util.h"
+#include "umask-util.h"
+#include "util.h"
+#include "virt.h"
static int shorten_uuid(char destination[34], const char source[36]) {
unsigned i, j;
@@ -188,7 +198,7 @@ static int generate_machine_id(char id[34], const char *root) {
return 0;
}
-int machine_id_setup(const char *root) {
+int machine_id_setup(const char *root, sd_id128_t machine_id) {
const char *etc_machine_id, *run_machine_id;
_cleanup_close_ int fd = -1;
bool writable = true;
@@ -238,15 +248,22 @@ int machine_id_setup(const char *root) {
}
}
- if (read_machine_id(fd, id) >= 0)
- return 0;
+ /* A machine id argument overrides all other machined-ids */
+ if (!sd_id128_is_null(machine_id)) {
+ sd_id128_to_string(machine_id, id);
+ id[32] = '\n';
+ id[33] = 0;
+ } else {
+ if (read_machine_id(fd, id) >= 0)
+ return 0;
- /* Hmm, so, the id currently stored is not useful, then let's
- * generate one */
+ /* 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;
+ r = generate_machine_id(id, root);
+ if (r < 0)
+ return r;
+ }
if (writable)
if (write_machine_id(fd, id) >= 0)
diff --git a/src/core/machine-id-setup.h b/src/core/machine-id-setup.h
index f7707c3bf9..a2168a8d4a 100644
--- a/src/core/machine-id-setup.h
+++ b/src/core/machine-id-setup.h
@@ -22,4 +22,4 @@
***/
int machine_id_commit(const char *root);
-int machine_id_setup(const char *root);
+int machine_id_setup(const char *root, sd_id128_t machine_id);
diff --git a/src/core/macros.systemd.in b/src/core/macros.systemd.in
index bea6ef1da3..2cace3d3ba 100644
--- a/src/core/macros.systemd.in
+++ b/src/core/macros.systemd.in
@@ -1,4 +1,4 @@
-# -*- Mode: makefile; indent-tabs-mode: t -*- */
+# -*- Mode: rpm-spec; indent-tabs-mode: nil -*- */
#
# This file is part of systemd.
#
@@ -39,17 +39,16 @@ Requires(postun): systemd \
%systemd_post() \
if [ $1 -eq 1 ] ; then \
# Initial installation \
- systemctl preset %{?*} >/dev/null 2>&1 || : \
+ systemctl --no-reload preset %{?*} >/dev/null 2>&1 || : \
fi \
%{nil}
-%systemd_user_post() %systemd_post --user --global %{?*}
+%systemd_user_post() %{expand:%systemd_post \\--user \\--global %%{?*}}
%systemd_preun() \
if [ $1 -eq 0 ] ; then \
# Package removal, not upgrade \
- systemctl --no-reload disable %{?*} > /dev/null 2>&1 || : \
- systemctl stop %{?*} > /dev/null 2>&1 || : \
+ systemctl --no-reload disable --now %{?*} > /dev/null 2>&1 || : \
fi \
%{nil}
@@ -60,14 +59,11 @@ if [ $1 -eq 0 ] ; then \
fi \
%{nil}
-%systemd_postun() \
-systemctl daemon-reload >/dev/null 2>&1 || : \
-%{nil}
+%systemd_postun() %{nil}
%systemd_user_postun() %{nil}
%systemd_postun_with_restart() \
-systemctl daemon-reload >/dev/null 2>&1 || : \
if [ $1 -ge 1 ] ; then \
# Package upgrade, not uninstall \
systemctl try-restart %{?*} >/dev/null 2>&1 || : \
diff --git a/src/core/main.c b/src/core/main.c
index b57f4c1b7a..27ba6af031 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -19,63 +19,73 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
#include <errno.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/stat.h>
+#include <fcntl.h>
#include <getopt.h>
#include <signal.h>
-#include <fcntl.h>
-#include <sys/prctl.h>
+#include <stdio.h>
+#include <string.h>
#include <sys/mount.h>
-
-#ifdef HAVE_VALGRIND_VALGRIND_H
-#include <valgrind/valgrind.h>
-#endif
+#include <sys/prctl.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <unistd.h>
#ifdef HAVE_SECCOMP
#include <seccomp.h>
#endif
+#ifdef HAVE_VALGRIND_VALGRIND_H
+#include <valgrind/valgrind.h>
+#endif
-#include "sd-daemon.h"
#include "sd-bus.h"
-#include "log.h"
-#include "fdset.h"
-#include "special.h"
-#include "conf-parser.h"
-#include "missing.h"
-#include "pager.h"
+#include "sd-daemon.h"
+
+#include "alloc-util.h"
+#include "architecture.h"
#include "build.h"
-#include "strv.h"
+#include "bus-error.h"
+#include "bus-util.h"
+#include "capability-util.h"
+#include "clock-util.h"
+#include "conf-parser.h"
+#include "cpu-set-util.h"
+#include "dbus-manager.h"
#include "def.h"
-#include "virt.h"
-#include "architecture.h"
-#include "watchdog.h"
-#include "switch-root.h"
-#include "capability.h"
-#include "killall.h"
#include "env-util.h"
-#include "clock-util.h"
+#include "fd-util.h"
+#include "fdset.h"
#include "fileio.h"
-#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 "fs-util.h"
+#include "hostname-setup.h"
+#include "ima-setup.h"
+#include "killall.h"
+#include "kmod-setup.h"
#include "load-fragment.h"
-
-#include "mount-setup.h"
+#include "log.h"
#include "loopback-setup.h"
-#include "hostname-setup.h"
#include "machine-id-setup.h"
+#include "manager.h"
+#include "missing.h"
+#include "mount-setup.h"
+#include "pager.h"
+#include "parse-util.h"
+#include "proc-cmdline.h"
+#include "process-util.h"
+#include "rlimit-util.h"
#include "selinux-setup.h"
-#include "ima-setup.h"
+#include "selinux-util.h"
+#include "signal-util.h"
#include "smack-setup.h"
-#include "kmod-setup.h"
+#include "special.h"
+#include "stat-util.h"
+#include "stdio-util.h"
+#include "strv.h"
+#include "switch-root.h"
+#include "terminal-util.h"
+#include "user-util.h"
+#include "virt.h"
+#include "watchdog.h"
static enum {
ACTION_RUN,
@@ -88,8 +98,9 @@ static enum {
static char *arg_default_unit = NULL;
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;
+static bool arg_crash_shell = false;
+static bool arg_crash_reboot = false;
static bool arg_confirm_spawn = false;
static ShowStatus arg_show_status = _SHOW_STATUS_UNSET;
static bool arg_switched_root = false;
@@ -106,7 +117,7 @@ static usec_t arg_runtime_watchdog = 0;
static usec_t arg_shutdown_watchdog = 10 * USEC_PER_MINUTE;
static char **arg_default_environment = NULL;
static struct rlimit *arg_default_rlimit[_RLIMIT_MAX] = {};
-static uint64_t arg_capability_bounding_set_drop = 0;
+static uint64_t arg_capability_bounding_set = CAP_ALL;
static nsec_t arg_timer_slack_nsec = NSEC_INFINITY;
static usec_t arg_default_timer_accuracy_usec = 1 * USEC_PER_MINUTE;
static Set* arg_syscall_archs = NULL;
@@ -114,9 +125,9 @@ static FILE* arg_serialization = NULL;
static bool arg_default_cpu_accounting = false;
static bool arg_default_blockio_accounting = false;
static bool arg_default_memory_accounting = false;
-static bool arg_default_tasks_accounting = false;
-
-static void nop_handler(int sig) {}
+static bool arg_default_tasks_accounting = true;
+static uint64_t arg_default_tasks_max = UINT64_C(512);
+static sd_id128_t arg_machine_id = {};
static void pager_open_if_enabled(void) {
@@ -126,49 +137,66 @@ static void pager_open_if_enabled(void) {
pager_open(false);
}
+noreturn static void freeze_or_reboot(void) {
+
+ if (arg_crash_reboot) {
+ log_notice("Rebooting in 10s...");
+ (void) sleep(10);
+
+ log_notice("Rebooting now...");
+ (void) reboot(RB_AUTOBOOT);
+ log_emergency_errno(errno, "Failed to reboot: %m");
+ }
+
+ log_emergency("Freezing execution.");
+ freeze();
+}
+
noreturn static void crash(int sig) {
+ struct sigaction sa;
+ pid_t pid;
if (getpid() != 1)
/* Pass this on immediately, if this is not PID 1 */
- raise(sig);
+ (void) raise(sig);
else if (!arg_dump_core)
log_emergency("Caught <%s>, not dumping core.", signal_to_string(sig));
else {
- struct sigaction sa = {
- .sa_handler = nop_handler,
+ sa = (struct sigaction) {
+ .sa_handler = nop_signal_handler,
.sa_flags = SA_NOCLDSTOP|SA_RESTART,
};
- pid_t pid;
/* We want to wait for the core process, hence let's enable SIGCHLD */
- sigaction(SIGCHLD, &sa, NULL);
+ (void) sigaction(SIGCHLD, &sa, NULL);
pid = raw_clone(SIGCHLD, NULL);
if (pid < 0)
log_emergency_errno(errno, "Caught <%s>, cannot fork for core dump: %m", signal_to_string(sig));
-
else if (pid == 0) {
- struct rlimit rl = {};
+ struct rlimit rl = {
+ .rlim_cur = RLIM_INFINITY,
+ .rlim_max = RLIM_INFINITY,
+ };
/* Enable default signal handler for core dump */
- zero(sa);
- sa.sa_handler = SIG_DFL;
- sigaction(sig, &sa, NULL);
+ sa = (struct sigaction) {
+ .sa_handler = SIG_DFL,
+ };
+ (void) sigaction(sig, &sa, NULL);
/* Don't limit the core dump size */
- rl.rlim_cur = RLIM_INFINITY;
- rl.rlim_max = RLIM_INFINITY;
- setrlimit(RLIMIT_CORE, &rl);
+ (void) setrlimit(RLIMIT_CORE, &rl);
/* Just to be sure... */
(void) chdir("/");
/* Raise the signal again */
pid = raw_getpid();
- kill(pid, sig); /* raise() would kill the parent */
+ (void) kill(pid, sig); /* raise() would kill the parent */
assert_not_reached("We shouldn't be here...");
- _exit(1);
+ _exit(EXIT_FAILURE);
} else {
siginfo_t status;
int r;
@@ -190,37 +218,38 @@ noreturn static void crash(int sig) {
}
}
- if (arg_crash_chvt)
- chvt(arg_crash_chvt);
+ if (arg_crash_chvt >= 0)
+ (void) chvt(arg_crash_chvt);
- if (arg_crash_shell) {
- struct sigaction sa = {
- .sa_handler = SIG_IGN,
- .sa_flags = SA_NOCLDSTOP|SA_NOCLDWAIT|SA_RESTART,
- };
- pid_t pid;
+ sa = (struct sigaction) {
+ .sa_handler = SIG_IGN,
+ .sa_flags = SA_NOCLDSTOP|SA_NOCLDWAIT|SA_RESTART,
+ };
- log_info("Executing crash shell in 10s...");
- sleep(10);
+ /* Let the kernel reap children for us */
+ (void) sigaction(SIGCHLD, &sa, NULL);
- /* Let the kernel reap children for us */
- assert_se(sigaction(SIGCHLD, &sa, NULL) == 0);
+ if (arg_crash_shell) {
+ log_notice("Executing crash shell in 10s...");
+ (void) sleep(10);
pid = raw_clone(SIGCHLD, NULL);
if (pid < 0)
log_emergency_errno(errno, "Failed to fork off crash shell: %m");
else if (pid == 0) {
- make_console_stdio();
- execle("/bin/sh", "/bin/sh", NULL, environ);
+ (void) setsid();
+ (void) make_console_stdio();
+ (void) execle("/bin/sh", "/bin/sh", NULL, environ);
log_emergency_errno(errno, "execle() failed: %m");
- _exit(1);
- } else
- log_info("Successfully spawned crash shell as PID "PID_FMT".", pid);
+ _exit(EXIT_FAILURE);
+ } else {
+ log_info("Spawned crash shell as PID "PID_FMT".", pid);
+ (void) wait_for_terminate(pid, NULL);
+ }
}
- log_emergency("Freezing execution.");
- freeze();
+ freeze_or_reboot();
}
static void install_crash_handler(void) {
@@ -254,37 +283,37 @@ static int console_setup(void) {
return 0;
}
-static int set_default_unit(const char *u) {
- char *c;
+static int parse_crash_chvt(const char *value) {
+ int b;
- assert(u);
+ if (safe_atoi(value, &arg_crash_chvt) >= 0)
+ return 0;
- c = strdup(u);
- if (!c)
- return -ENOMEM;
+ b = parse_boolean(value);
+ if (b < 0)
+ return b;
+
+ if (b > 0)
+ arg_crash_chvt = 0; /* switch to where kmsg goes */
+ else
+ arg_crash_chvt = -1; /* turn off switching */
+
+ return 0;
+}
- free(arg_default_unit);
- arg_default_unit = c;
+static int set_machine_id(const char *m) {
+
+ if (sd_id128_from_string(m, &arg_machine_id) < 0)
+ return -EINVAL;
+
+ if (sd_id128_is_null(arg_machine_id))
+ return -EINVAL;
return 0;
}
static int parse_proc_cmdline_item(const char *key, const char *value) {
- static const char * const rlmap[] = {
- "emergency", SPECIAL_EMERGENCY_TARGET,
- "-b", SPECIAL_EMERGENCY_TARGET,
- "rescue", SPECIAL_RESCUE_TARGET,
- "single", SPECIAL_RESCUE_TARGET,
- "-s", SPECIAL_RESCUE_TARGET,
- "s", SPECIAL_RESCUE_TARGET,
- "S", SPECIAL_RESCUE_TARGET,
- "1", SPECIAL_RESCUE_TARGET,
- "2", SPECIAL_MULTI_USER_TARGET,
- "3", SPECIAL_MULTI_USER_TARGET,
- "4", SPECIAL_MULTI_USER_TARGET,
- "5", SPECIAL_GRAPHICAL_TARGET,
- };
int r;
assert(key);
@@ -292,12 +321,12 @@ static int parse_proc_cmdline_item(const char *key, const char *value) {
if (streq(key, "systemd.unit") && value) {
if (!in_initrd())
- return set_default_unit(value);
+ return free_and_strdup(&arg_default_unit, value);
} else if (streq(key, "rd.systemd.unit") && value) {
if (in_initrd())
- return set_default_unit(value);
+ return free_and_strdup(&arg_default_unit, value);
} else if (streq(key, "systemd.dump_core") && value) {
@@ -307,6 +336,11 @@ static int parse_proc_cmdline_item(const char *key, const char *value) {
else
arg_dump_core = r;
+ } else if (streq(key, "systemd.crash_chvt") && value) {
+
+ if (parse_crash_chvt(value) < 0)
+ log_warning("Failed to parse crash chvt switch %s. Ignoring.", value);
+
} else if (streq(key, "systemd.crash_shell") && value) {
r = parse_boolean(value);
@@ -315,12 +349,13 @@ static int parse_proc_cmdline_item(const char *key, const char *value) {
else
arg_crash_shell = r;
- } else if (streq(key, "systemd.crash_chvt") && value) {
+ } else if (streq(key, "systemd.crash_reboot") && value) {
- if (safe_atoi(value, &r) < 0)
- log_warning("Failed to parse crash chvt switch %s. Ignoring.", value);
+ r = parse_boolean(value);
+ if (r < 0)
+ log_warning("Failed to parse crash reboot switch %s. Ignoring.", value);
else
- arg_crash_chvt = r;
+ arg_crash_reboot = r;
} else if (streq(key, "systemd.confirm_spawn") && value) {
@@ -365,6 +400,12 @@ static int parse_proc_cmdline_item(const char *key, const char *value) {
} else
log_warning("Environment variable name '%s' is not valid. Ignoring.", value);
+ } else if (streq(key, "systemd.machine_id") && value) {
+
+ r = set_machine_id(value);
+ if (r < 0)
+ log_warning("MachineID '%s' is not valid. Ignoring.", value);
+
} else if (streq(key, "quiet") && !value) {
if (arg_show_status == _SHOW_STATUS_UNSET)
@@ -379,12 +420,12 @@ static int parse_proc_cmdline_item(const char *key, const char *value) {
log_set_target(LOG_TARGET_CONSOLE);
} else if (!in_initrd() && !value) {
- unsigned i;
+ const char *target;
/* SysV compatibility */
- for (i = 0; i < ELEMENTSOF(rlmap); i += 2)
- if (streq(key, rlmap[i]))
- return set_default_unit(rlmap[i+1]);
+ target = runlevel_to_target(key);
+ if (target)
+ return free_and_strdup(&arg_default_unit, target);
}
return 0;
@@ -410,9 +451,9 @@ static int parse_proc_cmdline_item(const char *key, const char *value) {
\
r = func(rvalue); \
if (r < 0) \
- log_syntax(unit, LOG_ERR, filename, line, -r, \
- "Invalid " descr "'%s': %s", \
- rvalue, strerror(-r)); \
+ log_syntax(unit, LOG_ERR, filename, line, r, \
+ "Invalid " descr "'%s': %m", \
+ rvalue); \
\
return 0; \
}
@@ -434,48 +475,15 @@ static int config_parse_cpu_affinity2(
void *data,
void *userdata) {
- const char *whole_rvalue = rvalue;
_cleanup_cpu_free_ cpu_set_t *c = NULL;
- unsigned ncpus = 0;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
-
- for (;;) {
- _cleanup_free_ char *word = NULL;
- unsigned cpu;
- int r;
-
- r = extract_first_word(&rvalue, &word, WHITESPACE, EXTRACT_QUOTES);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Invalid value for %s: %s", lvalue, whole_rvalue);
- return r;
- }
- if (r == 0)
- break;
+ int ncpus;
- r = safe_atou(word, &cpu);
+ ncpus = parse_cpu_set_and_warn(rvalue, &c, unit, filename, line, lvalue);
+ if (ncpus < 0)
+ return ncpus;
- if (!c)
- if (!(c = cpu_set_malloc(&ncpus)))
- return log_oom();
-
- if (r < 0 || cpu >= ncpus) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to parse CPU affinity '%s'", rvalue);
- return -EBADMSG;
- }
-
- CPU_SET_S(cpu, CPU_ALLOC_SIZE(ncpus), c);
- }
- if (!isempty(rvalue))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Trailing garbage, ignoring.");
-
- if (c)
- if (sched_setaffinity(0, CPU_ALLOC_SIZE(ncpus), c) < 0)
- log_warning("Failed to set CPU affinity: %m");
+ if (sched_setaffinity(0, CPU_ALLOC_SIZE(ncpus), c) < 0)
+ log_warning("Failed to set CPU affinity: %m");
return 0;
}
@@ -502,29 +510,38 @@ static int config_parse_show_status(
k = parse_show_status(rvalue, b);
if (k < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -k,
- "Failed to parse show status setting, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse show status setting, ignoring: %s", rvalue);
return 0;
}
return 0;
}
-static void strv_free_free(char ***l) {
- char ***i;
+static int config_parse_crash_chvt(
+ 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) {
- if (!l)
- return;
+ int r;
- for (i = l; *i; i++)
- strv_free(*i);
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
- free(l);
-}
+ r = parse_crash_chvt(rvalue);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse CrashChangeVT= setting, ignoring: %s", rvalue);
+ return 0;
+ }
-static void free_join_controllers(void) {
- strv_free_free(arg_join_controllers);
- arg_join_controllers = NULL;
+ return 0;
}
static int config_parse_join_controllers(const char *unit,
@@ -545,7 +562,7 @@ static int config_parse_join_controllers(const char *unit,
assert(lvalue);
assert(rvalue);
- free_join_controllers();
+ arg_join_controllers = strv_free_free(arg_join_controllers);
for (;;) {
_cleanup_free_ char *word = NULL;
@@ -562,7 +579,7 @@ static int config_parse_join_controllers(const char *unit,
l = strv_split(word, ",");
if (!l)
- log_oom();
+ return log_oom();
strv_uniq(l);
if (strv_length(l) <= 1) {
@@ -596,7 +613,7 @@ static int config_parse_join_controllers(const char *unit,
for (a = arg_join_controllers; *a; a++) {
if (strv_overlap(*a, l)) {
- if (strv_extend_strv(&l, *a) < 0) {
+ if (strv_extend_strv(&l, *a, false) < 0) {
strv_free(l);
strv_free_free(t);
return log_oom();
@@ -623,8 +640,7 @@ static int config_parse_join_controllers(const char *unit,
}
}
if (!isempty(rvalue))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Trailing garbage, ignoring.");
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
return 0;
}
@@ -637,14 +653,16 @@ static int parse_config_file(void) {
{ "Manager", "LogColor", config_parse_color, 0, NULL },
{ "Manager", "LogLocation", config_parse_location, 0, NULL },
{ "Manager", "DumpCore", config_parse_bool, 0, &arg_dump_core },
+ { "Manager", "CrashChVT", /* legacy */ config_parse_crash_chvt, 0, NULL },
+ { "Manager", "CrashChangeVT", config_parse_crash_chvt, 0, NULL },
{ "Manager", "CrashShell", config_parse_bool, 0, &arg_crash_shell },
+ { "Manager", "CrashReboot", config_parse_bool, 0, &arg_crash_reboot },
{ "Manager", "ShowStatus", config_parse_show_status, 0, &arg_show_status },
- { "Manager", "CrashChVT", config_parse_int, 0, &arg_crash_chvt },
{ "Manager", "CPUAffinity", config_parse_cpu_affinity2, 0, NULL },
{ "Manager", "JoinControllers", config_parse_join_controllers, 0, &arg_join_controllers },
{ "Manager", "RuntimeWatchdogSec", config_parse_sec, 0, &arg_runtime_watchdog },
{ "Manager", "ShutdownWatchdogSec", config_parse_sec, 0, &arg_shutdown_watchdog },
- { "Manager", "CapabilityBoundingSet", config_parse_bounding_set, 0, &arg_capability_bounding_set_drop },
+ { "Manager", "CapabilityBoundingSet", config_parse_capability_set, 0, &arg_capability_bounding_set },
#ifdef HAVE_SECCOMP
{ "Manager", "SystemCallArchitectures", config_parse_syscall_archs, 0, &arg_syscall_archs },
#endif
@@ -658,33 +676,40 @@ static int parse_config_file(void) {
{ "Manager", "DefaultStartLimitInterval", config_parse_sec, 0, &arg_default_start_limit_interval },
{ "Manager", "DefaultStartLimitBurst", config_parse_unsigned, 0, &arg_default_start_limit_burst },
{ "Manager", "DefaultEnvironment", config_parse_environ, 0, &arg_default_environment },
- { "Manager", "DefaultLimitCPU", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_CPU] },
- { "Manager", "DefaultLimitFSIZE", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_FSIZE] },
- { "Manager", "DefaultLimitDATA", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_DATA] },
- { "Manager", "DefaultLimitSTACK", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_STACK] },
- { "Manager", "DefaultLimitCORE", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_CORE] },
- { "Manager", "DefaultLimitRSS", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_RSS] },
+ { "Manager", "DefaultLimitCPU", config_parse_sec_limit, 0, &arg_default_rlimit[RLIMIT_CPU] },
+ { "Manager", "DefaultLimitFSIZE", config_parse_bytes_limit, 0, &arg_default_rlimit[RLIMIT_FSIZE] },
+ { "Manager", "DefaultLimitDATA", config_parse_bytes_limit, 0, &arg_default_rlimit[RLIMIT_DATA] },
+ { "Manager", "DefaultLimitSTACK", config_parse_bytes_limit, 0, &arg_default_rlimit[RLIMIT_STACK] },
+ { "Manager", "DefaultLimitCORE", config_parse_bytes_limit, 0, &arg_default_rlimit[RLIMIT_CORE] },
+ { "Manager", "DefaultLimitRSS", config_parse_bytes_limit, 0, &arg_default_rlimit[RLIMIT_RSS] },
{ "Manager", "DefaultLimitNOFILE", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_NOFILE] },
- { "Manager", "DefaultLimitAS", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_AS] },
+ { "Manager", "DefaultLimitAS", config_parse_bytes_limit, 0, &arg_default_rlimit[RLIMIT_AS] },
{ "Manager", "DefaultLimitNPROC", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_NPROC] },
- { "Manager", "DefaultLimitMEMLOCK", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_MEMLOCK] },
+ { "Manager", "DefaultLimitMEMLOCK", config_parse_bytes_limit, 0, &arg_default_rlimit[RLIMIT_MEMLOCK] },
{ "Manager", "DefaultLimitLOCKS", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_LOCKS] },
{ "Manager", "DefaultLimitSIGPENDING", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_SIGPENDING] },
- { "Manager", "DefaultLimitMSGQUEUE", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_MSGQUEUE] },
+ { "Manager", "DefaultLimitMSGQUEUE", config_parse_bytes_limit, 0, &arg_default_rlimit[RLIMIT_MSGQUEUE] },
{ "Manager", "DefaultLimitNICE", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_NICE] },
{ "Manager", "DefaultLimitRTPRIO", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_RTPRIO] },
- { "Manager", "DefaultLimitRTTIME", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_RTTIME] },
+ { "Manager", "DefaultLimitRTTIME", config_parse_usec_limit, 0, &arg_default_rlimit[RLIMIT_RTTIME] },
{ "Manager", "DefaultCPUAccounting", config_parse_bool, 0, &arg_default_cpu_accounting },
{ "Manager", "DefaultBlockIOAccounting", config_parse_bool, 0, &arg_default_blockio_accounting },
{ "Manager", "DefaultMemoryAccounting", config_parse_bool, 0, &arg_default_memory_accounting },
{ "Manager", "DefaultTasksAccounting", config_parse_bool, 0, &arg_default_tasks_accounting },
+ { "Manager", "DefaultTasksMax", config_parse_tasks_max, 0, &arg_default_tasks_max },
{}
};
const char *fn, *conf_dirs_nulstr;
- 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");
+ fn = arg_running_as == MANAGER_SYSTEM ?
+ PKGSYSCONFDIR "/system.conf" :
+ PKGSYSCONFDIR "/user.conf";
+
+ conf_dirs_nulstr = arg_running_as == MANAGER_SYSTEM ?
+ CONF_PATHS_NULSTR("systemd/system.conf.d") :
+ CONF_PATHS_NULSTR("systemd/user.conf.d");
+
config_parse_many(fn, conf_dirs_nulstr, "Manager\0",
config_item_table_lookup, items, false, NULL);
@@ -707,6 +732,7 @@ static void manager_set_defaults(Manager *m) {
m->default_blockio_accounting = arg_default_blockio_accounting;
m->default_memory_accounting = arg_default_memory_accounting;
m->default_tasks_accounting = arg_default_tasks_accounting;
+ m->default_tasks_max = arg_default_tasks_max;
manager_set_default_rlimits(m, arg_default_rlimit);
manager_environment_add(m, NULL, arg_default_environment);
@@ -727,13 +753,16 @@ static int parse_argv(int argc, char *argv[]) {
ARG_VERSION,
ARG_DUMP_CONFIGURATION_ITEMS,
ARG_DUMP_CORE,
+ ARG_CRASH_CHVT,
ARG_CRASH_SHELL,
+ ARG_CRASH_REBOOT,
ARG_CONFIRM_SPAWN,
ARG_SHOW_STATUS,
ARG_DESERIALIZE,
ARG_SWITCHED_ROOT,
ARG_DEFAULT_STD_OUTPUT,
- ARG_DEFAULT_STD_ERROR
+ ARG_DEFAULT_STD_ERROR,
+ ARG_MACHINE_ID
};
static const struct option options[] = {
@@ -750,13 +779,16 @@ static int parse_argv(int argc, char *argv[]) {
{ "version", no_argument, NULL, ARG_VERSION },
{ "dump-configuration-items", no_argument, NULL, ARG_DUMP_CONFIGURATION_ITEMS },
{ "dump-core", optional_argument, NULL, ARG_DUMP_CORE },
+ { "crash-chvt", required_argument, NULL, ARG_CRASH_CHVT },
{ "crash-shell", optional_argument, NULL, ARG_CRASH_SHELL },
+ { "crash-reboot", optional_argument, NULL, ARG_CRASH_REBOOT },
{ "confirm-spawn", optional_argument, NULL, ARG_CONFIRM_SPAWN },
{ "show-status", optional_argument, NULL, ARG_SHOW_STATUS },
{ "deserialize", required_argument, NULL, ARG_DESERIALIZE },
{ "switched-root", no_argument, NULL, ARG_SWITCHED_ROOT },
{ "default-standard-output", required_argument, NULL, ARG_DEFAULT_STD_OUTPUT, },
{ "default-standard-error", required_argument, NULL, ARG_DEFAULT_STD_ERROR, },
+ { "machine-id", required_argument, NULL, ARG_MACHINE_ID },
{}
};
@@ -835,7 +867,7 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_UNIT:
- r = set_default_unit(optarg);
+ r = free_and_strdup(&arg_default_unit, optarg);
if (r < 0)
return log_error_errno(r, "Failed to set default unit %s: %m", optarg);
@@ -868,21 +900,42 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_DUMP_CORE:
- r = optarg ? parse_boolean(optarg) : 1;
- if (r < 0) {
- log_error("Failed to parse dump core boolean %s.", optarg);
- return r;
+ if (!optarg)
+ arg_dump_core = true;
+ else {
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse dump core boolean: %s", optarg);
+ arg_dump_core = r;
}
- arg_dump_core = r;
+ break;
+
+ case ARG_CRASH_CHVT:
+ r = parse_crash_chvt(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse crash virtual terminal index: %s", optarg);
break;
case ARG_CRASH_SHELL:
- r = optarg ? parse_boolean(optarg) : 1;
- if (r < 0) {
- log_error("Failed to parse crash shell boolean %s.", optarg);
- return r;
+ if (!optarg)
+ arg_crash_shell = true;
+ else {
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse crash shell boolean: %s", optarg);
+ arg_crash_shell = r;
+ }
+ break;
+
+ case ARG_CRASH_REBOOT:
+ if (!optarg)
+ arg_crash_reboot = true;
+ else {
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse crash shell boolean: %s", optarg);
+ arg_crash_reboot = r;
}
- arg_crash_shell = r;
break;
case ARG_CONFIRM_SPAWN:
@@ -912,17 +965,16 @@ static int parse_argv(int argc, char *argv[]) {
r = safe_atoi(optarg, &fd);
if (r < 0 || fd < 0) {
log_error("Failed to parse deserialize option %s.", optarg);
- return r < 0 ? r : -EINVAL;
+ return -EINVAL;
}
- fd_cloexec(fd, true);
+ (void) fd_cloexec(fd, true);
f = fdopen(fd, "r");
if (!f)
return log_error_errno(errno, "Failed to open serialization fd: %m");
safe_fclose(arg_serialization);
-
arg_serialization = f;
break;
@@ -932,6 +984,14 @@ static int parse_argv(int argc, char *argv[]) {
arg_switched_root = true;
break;
+ case ARG_MACHINE_ID:
+ r = set_machine_id(optarg);
+ if (r < 0) {
+ log_error("MachineID '%s' is not valid.", optarg);
+ return r;
+ }
+ break;
+
case 'h':
arg_action = ACTION_HELP;
if (arg_no_pager < 0)
@@ -982,14 +1042,16 @@ static int help(void) {
" --unit=UNIT Set default unit\n"
" --system Run a system instance, even if PID != 1\n"
" --user Run a user instance\n"
- " --dump-core[=0|1] Dump core on crash\n"
- " --crash-shell[=0|1] Run shell on crash\n"
- " --confirm-spawn[=0|1] Ask for confirmation when spawning processes\n"
- " --show-status[=0|1] Show status updates on the console during bootup\n"
+ " --dump-core[=BOOL] Dump core on crash\n"
+ " --crash-vt=NR Change to specified VT on crash\n"
+ " --crash-reboot[=BOOL] Reboot on crash\n"
+ " --crash-shell[=BOOL] Run shell on crash\n"
+ " --confirm-spawn[=BOOL] Ask for confirmation when spawning processes\n"
+ " --show-status[=BOOL] Show status updates on the console during bootup\n"
" --log-target=TARGET Set log target (console, journal, kmsg, journal-or-kmsg, null)\n"
" --log-level=LEVEL Set log level (debug, info, notice, warning, err, crit, alert, emerg)\n"
- " --log-color[=0|1] Highlight important log messages\n"
- " --log-location[=0|1] Include code location in log messages\n"
+ " --log-color[=BOOL] Highlight important log messages\n"
+ " --log-location[=BOOL] Include code location in log messages\n"
" --default-standard-output= Set default standard output for services\n"
" --default-standard-error= Set default standard error output for services\n",
program_invocation_short_name);
@@ -997,16 +1059,9 @@ static int help(void) {
return 0;
}
-static int version(void) {
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
-
- return 0;
-}
-
static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds, bool switching_root) {
- FILE *f = NULL;
- FDSet *fds = NULL;
+ _cleanup_fdset_free_ FDSet *fds = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
int r;
assert(m);
@@ -1014,56 +1069,39 @@ static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds, bool switching
assert(_fds);
r = manager_open_serialization(m, &f);
- if (r < 0) {
- log_error_errno(r, "Failed to create serialization file: %m");
- goto fail;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to create serialization file: %m");
/* Make sure nothing is really destructed when we shut down */
m->n_reloading ++;
bus_manager_send_reloading(m, true);
fds = fdset_new();
- if (!fds) {
- r = -ENOMEM;
- log_error_errno(r, "Failed to allocate fd set: %m");
- goto fail;
- }
+ if (!fds)
+ return log_oom();
r = manager_serialize(m, f, fds, switching_root);
- if (r < 0) {
- log_error_errno(r, "Failed to serialize state: %m");
- goto fail;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to serialize state: %m");
- if (fseeko(f, 0, SEEK_SET) < 0) {
- log_error_errno(errno, "Failed to rewind serialization fd: %m");
- goto fail;
- }
+ if (fseeko(f, 0, SEEK_SET) == (off_t) -1)
+ return log_error_errno(errno, "Failed to rewind serialization fd: %m");
r = fd_cloexec(fileno(f), false);
- if (r < 0) {
- log_error_errno(r, "Failed to disable O_CLOEXEC for serialization: %m");
- goto fail;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to disable O_CLOEXEC for serialization: %m");
r = fdset_cloexec(fds, false);
- if (r < 0) {
- log_error_errno(r, "Failed to disable O_CLOEXEC for serialization fds: %m");
- goto fail;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to disable O_CLOEXEC for serialization fds: %m");
*_f = f;
*_fds = fds;
- return 0;
-
-fail:
- fdset_free(fds);
-
- safe_fclose(f);
+ f = NULL;
+ fds = NULL;
- return r;
+ return 0;
}
static int bump_rlimit_nofile(struct rlimit *saved_rlimit) {
@@ -1098,33 +1136,6 @@ static int bump_rlimit_nofile(struct rlimit *saved_rlimit) {
return 0;
}
-static void test_mtab(void) {
-
- static const char ok[] =
- "/proc/self/mounts\0"
- "/proc/mounts\0"
- "../proc/self/mounts\0"
- "../proc/mounts\0";
-
- _cleanup_free_ char *p = NULL;
- int r;
-
- /* Check that /etc/mtab is a symlink to the right place or
- * non-existing. But certainly not a file, or a symlink to
- * some weird place... */
-
- r = readlink_malloc("/etc/mtab", &p);
- if (r == -ENOENT)
- return;
- if (r >= 0 && nulstr_contains(ok, p))
- return;
-
- log_error("/etc/mtab is not a symlink or not pointing to /proc/self/mounts. "
- "This is not supported anymore. "
- "Please make sure to replace this file by a symlink to avoid incorrect or misleading mount(8) output.");
- freeze();
-}
-
static void test_usr(void) {
/* Check that /usr is not a separate fs */
@@ -1148,15 +1159,19 @@ static int initialize_join_controllers(void) {
return -ENOMEM;
arg_join_controllers[0] = strv_new("cpu", "cpuacct", NULL);
- arg_join_controllers[1] = strv_new("net_cls", "net_prio", NULL);
- arg_join_controllers[2] = NULL;
+ if (!arg_join_controllers[0])
+ goto oom;
- if (!arg_join_controllers[0] || !arg_join_controllers[1]) {
- free_join_controllers();
- return -ENOMEM;
- }
+ arg_join_controllers[1] = strv_new("net_cls", "net_prio", NULL);
+ if (!arg_join_controllers[1])
+ goto oom;
+ arg_join_controllers[2] = NULL;
return 0;
+
+oom:
+ arg_join_controllers = strv_free_free(arg_join_controllers);
+ return -ENOMEM;
}
static int enforce_syscall_archs(Set *archs) {
@@ -1223,12 +1238,50 @@ static int status_welcome(void) {
static int write_container_id(void) {
const char *c;
+ int r;
c = getenv("container");
if (isempty(c))
return 0;
- return write_string_file("/run/systemd/container", c, WRITE_STRING_FILE_CREATE);
+ r = write_string_file("/run/systemd/container", c, WRITE_STRING_FILE_CREATE);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to write /run/systemd/container, ignoring: %m");
+
+ return 1;
+}
+
+static int bump_unix_max_dgram_qlen(void) {
+ _cleanup_free_ char *qlen = NULL;
+ unsigned long v;
+ int r;
+
+ /* Let's bump the net.unix.max_dgram_qlen sysctl. The kernel
+ * default of 16 is simply too low. We set the value really
+ * really early during boot, so that it is actually applied to
+ * all our sockets, including the $NOTIFY_SOCKET one. */
+
+ r = read_one_line_file("/proc/sys/net/unix/max_dgram_qlen", &qlen);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to read AF_UNIX datagram queue length, ignoring: %m");
+
+ r = safe_atolu(qlen, &v);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to parse AF_UNIX datagram queue length, ignoring: %m");
+
+ if (v >= DEFAULT_UNIX_MAX_DGRAM_QLEN)
+ return 0;
+
+ qlen = mfree(qlen);
+ if (asprintf(&qlen, "%lu\n", DEFAULT_UNIX_MAX_DGRAM_QLEN) < 0)
+ return log_oom();
+
+ r = write_string_file("/proc/sys/net/unix/max_dgram_qlen", qlen, 0);
+ if (r < 0)
+ return log_full_errno(IN_SET(r, -EROFS, -EPERM, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to bump AF_UNIX datagram queue length, ignoring: %m");
+
+ return 1;
}
int main(int argc, char *argv[]) {
@@ -1254,7 +1307,6 @@ int main(int argc, char *argv[]) {
char *switch_root_dir = NULL, *switch_root_init = NULL;
struct rlimit saved_rlimit_nofile = RLIMIT_MAKE_CONST(0);
const char *error_message = NULL;
- uint8_t shutdown_exit_code = 0;
#ifdef HAVE_SYSV_COMPAT
if (getpid() != 1 && strstr(program_invocation_short_name, "init")) {
@@ -1401,7 +1453,7 @@ int main(int argc, char *argv[]) {
}
/* Initialize default unit */
- r = set_default_unit(SPECIAL_DEFAULT_TARGET);
+ r = free_and_strdup(&arg_default_unit, SPECIAL_DEFAULT_TARGET);
if (r < 0) {
log_emergency_errno(r, "Failed to set default unit %s: %m", SPECIAL_DEFAULT_TARGET);
error_message = "Failed to set default unit";
@@ -1593,10 +1645,10 @@ int main(int argc, char *argv[]) {
status_welcome();
hostname_setup();
- machine_id_setup(NULL);
+ machine_id_setup(NULL, arg_machine_id);
loopback_setup();
+ bump_unix_max_dgram_qlen();
- test_mtab();
test_usr();
}
@@ -1607,14 +1659,14 @@ int main(int argc, char *argv[]) {
if (prctl(PR_SET_TIMERSLACK, arg_timer_slack_nsec) < 0)
log_error_errno(errno, "Failed to adjust timer slack: %m");
- if (arg_capability_bounding_set_drop) {
- r = capability_bounding_set_drop_usermode(arg_capability_bounding_set_drop);
+ if (!cap_test_all(arg_capability_bounding_set)) {
+ r = capability_bounding_set_drop_usermode(arg_capability_bounding_set);
if (r < 0) {
log_emergency_errno(r, "Failed to drop capability bounding set of usermode helpers: %m");
error_message = "Failed to drop capability bounding set of usermode helpers";
goto finish;
}
- r = capability_bounding_set_drop(arg_capability_bounding_set_drop, true);
+ r = capability_bounding_set_drop(arg_capability_bounding_set, true);
if (r < 0) {
log_emergency_errno(r, "Failed to drop capability bounding set: %m");
error_message = "Failed to drop capability bounding set";
@@ -1641,7 +1693,7 @@ int main(int argc, char *argv[]) {
if (empty_etc) {
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");
+ log_full_errno(r == -EEXIST ? LOG_NOTICE : LOG_WARNING, r, "Failed to populate /etc with preset unit settings, ignoring: %m");
else
log_info("Populated /etc with preset unit settings.");
}
@@ -1678,13 +1730,12 @@ int main(int argc, char *argv[]) {
/* This will close all file descriptors that were opened, but
* not claimed by any unit. */
- fdset_free(fds);
- fds = NULL;
+ fds = fdset_free(fds);
arg_serialization = safe_fclose(arg_serialization);
if (queue_default_job) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
Unit *target = NULL;
Job *default_unit_job;
@@ -1724,11 +1775,13 @@ int main(int argc, char *argv[]) {
manager_dump_units(m, stdout, "\t");
}
- r = manager_add_job(m, JOB_START, target, JOB_ISOLATE, false, &error, &default_unit_job);
+ r = manager_add_job(m, JOB_START, target, JOB_ISOLATE, &error, &default_unit_job);
if (r == -EPERM) {
log_debug("Default target could not be isolated, starting instead: %s", bus_error_message(&error, r));
- r = manager_add_job(m, JOB_START, target, JOB_REPLACE, false, &error, &default_unit_job);
+ sd_bus_error_free(&error);
+
+ r = manager_add_job(m, JOB_START, target, JOB_REPLACE, &error, &default_unit_job);
if (r < 0) {
log_emergency("Failed to start default target: %s", bus_error_message(&error, r));
error_message = "Failed to start default target";
@@ -1807,8 +1860,9 @@ int main(int argc, char *argv[]) {
goto finish;
case MANAGER_EXIT:
+ retval = m->return_value;
+
if (m->running_as == MANAGER_USER) {
- retval = EXIT_SUCCESS;
log_debug("Exit.");
goto finish;
}
@@ -1841,21 +1895,17 @@ int main(int argc, char *argv[]) {
finish:
pager_close();
- if (m) {
+ if (m)
arg_shutdown_watchdog = m->shutdown_watchdog;
- shutdown_exit_code = m->return_value;
- }
+
m = manager_free(m);
for (j = 0; j < ELEMENTSOF(arg_default_rlimit); j++)
arg_default_rlimit[j] = mfree(arg_default_rlimit[j]);
arg_default_unit = mfree(arg_default_unit);
-
- free_join_controllers();
-
+ arg_join_controllers = strv_free_free(arg_join_controllers);
arg_default_environment = strv_free(arg_default_environment);
-
arg_syscall_archs = set_free(arg_syscall_archs);
mac_selinux_finish();
@@ -1873,7 +1923,7 @@ finish:
* that the new systemd can pass the kernel default to
* its child processes */
if (saved_rlimit_nofile.rlim_cur > 0)
- setrlimit(RLIMIT_NOFILE, &saved_rlimit_nofile);
+ (void) setrlimit(RLIMIT_NOFILE, &saved_rlimit_nofile);
if (switch_root_dir) {
/* Kill all remaining processes from the
@@ -1915,10 +1965,19 @@ finish:
/* do not pass along the environment we inherit from the kernel or initrd */
if (switch_root_dir)
- clearenv();
+ (void) clearenv();
assert(i <= args_size);
- execv(args[0], (char* const*) args);
+
+ /*
+ * We want valgrind to print its memory usage summary before reexecution.
+ * Valgrind won't do this is on its own on exec(), but it will do it on exit().
+ * Hence, to ensure we get a summary here, fork() off a child, let it exit() cleanly,
+ * so that it prints the summary, and wait() for it in the parent, before proceeding into the exec().
+ */
+ valgrind_summary_hack();
+
+ (void) execv(args[0], (char* const*) args);
}
/* Try the fallback, if there is any, without any
@@ -1928,14 +1987,10 @@ finish:
* but let's hope that doesn't matter.) */
arg_serialization = safe_fclose(arg_serialization);
-
- if (fds) {
- fdset_free(fds);
- fds = NULL;
- }
+ fds = fdset_free(fds);
/* Reopen the console */
- make_console_stdio();
+ (void) make_console_stdio();
for (j = 1, i = 1; j < (unsigned) argc; j++)
args[i++] = argv[j];
@@ -1949,30 +2004,26 @@ finish:
if (switch_root_init) {
args[0] = switch_root_init;
- execv(args[0], (char* const*) args);
+ (void) execv(args[0], (char* const*) args);
log_warning_errno(errno, "Failed to execute configured init, trying fallback: %m");
}
args[0] = "/sbin/init";
- execv(args[0], (char* const*) args);
+ (void) execv(args[0], (char* const*) args);
if (errno == ENOENT) {
log_warning("No /sbin/init, trying fallback");
args[0] = "/bin/sh";
args[1] = NULL;
- execv(args[0], (char* const*) args);
+ (void) execv(args[0], (char* const*) args);
log_error_errno(errno, "Failed to execute /bin/sh, giving up: %m");
} else
log_warning_errno(errno, "Failed to execute /sbin/init, giving up: %m");
}
arg_serialization = safe_fclose(arg_serialization);
-
- if (fds) {
- fdset_free(fds);
- fds = NULL;
- }
+ fds = fdset_free(fds);
#ifdef HAVE_VALGRIND_VALGRIND_H
/* If we are PID 1 and running under valgrind, then let's exit
@@ -2001,6 +2052,7 @@ finish:
xsprintf(log_level, "%d", log_get_max_level());
switch (log_get_target()) {
+
case LOG_TARGET_KMSG:
case LOG_TARGET_JOURNAL_OR_KMSG:
case LOG_TARGET_SYSLOG_OR_KMSG:
@@ -2026,7 +2078,7 @@ finish:
if (streq(shutdown_verb, "exit")) {
command_line[pos++] = "--exit-code";
command_line[pos++] = exit_code;
- xsprintf(exit_code, "%d", shutdown_exit_code);
+ xsprintf(exit_code, "%d", retval);
}
assert(pos < ELEMENTSOF(command_line));
@@ -2042,7 +2094,7 @@ finish:
/* Tell the binary how often to ping, ignore failure */
if (asprintf(&e, "WATCHDOG_USEC="USEC_FMT, arg_shutdown_watchdog) > 0)
- strv_push(&env_block, e);
+ (void) strv_push(&env_block, e);
} else
watchdog_close(true);
@@ -2062,7 +2114,7 @@ finish:
manager_status_printf(NULL, STATUS_TYPE_EMERGENCY,
ANSI_HIGHLIGHT_RED "!!!!!!" ANSI_NORMAL,
"%s, freezing.", error_message);
- freeze();
+ freeze_or_reboot();
}
return retval;
diff --git a/src/core/manager.c b/src/core/manager.c
index 9bfe867ea0..a83a8b013a 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -19,19 +19,19 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <dirent.h>
#include <errno.h>
-#include <string.h>
+#include <fcntl.h>
+#include <linux/kd.h>
#include <signal.h>
-#include <sys/wait.h>
-#include <unistd.h>
-#include <sys/inotify.h>
+#include <string.h>
#include <sys/epoll.h>
-#include <sys/reboot.h>
+#include <sys/inotify.h>
#include <sys/ioctl.h>
-#include <linux/kd.h>
-#include <fcntl.h>
-#include <dirent.h>
+#include <sys/reboot.h>
#include <sys/timerfd.h>
+#include <sys/wait.h>
+#include <unistd.h>
#ifdef HAVE_AUDIT
#include <libaudit.h>
@@ -40,40 +40,53 @@
#include "sd-daemon.h"
#include "sd-messages.h"
-#include "hashmap.h"
-#include "macro.h"
-#include "strv.h"
-#include "log.h"
-#include "util.h"
-#include "mkdir.h"
-#include "ratelimit.h"
-#include "locale-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 "path-util.h"
+#include "alloc-util.h"
#include "audit-fd.h"
#include "boot-timestamps.h"
-#include "env-util.h"
#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 "bus-util.h"
#include "dbus-job.h"
#include "dbus-manager.h"
+#include "dbus-unit.h"
+#include "dbus.h"
+#include "env-util.h"
+#include "escape.h"
+#include "exit-status.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "hashmap.h"
+#include "io-util.h"
+#include "locale-setup.h"
+#include "log.h"
+#include "macro.h"
#include "manager.h"
+#include "missing.h"
+#include "mkdir.h"
+#include "parse-util.h"
+#include "path-lookup.h"
+#include "path-util.h"
+#include "process-util.h"
+#include "ratelimit.h"
+#include "rm-rf.h"
+#include "signal-util.h"
+#include "special.h"
+#include "stat-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "strv.h"
+#include "terminal-util.h"
+#include "time-util.h"
#include "transaction.h"
+#include "umask-util.h"
+#include "unit-name.h"
+#include "util.h"
+#include "virt.h"
+#include "watchdog.h"
+
+#define NOTIFY_RCVBUF_SIZE (8*1024*1024)
/* Initial delay and the interval for printing status messages about running jobs */
#define JOBS_IN_PROGRESS_WAIT_USEC (5*USEC_PER_SEC)
@@ -220,7 +233,7 @@ static int have_ask_password(void) {
errno = 0;
de = readdir(dir);
- if (!de && errno != 0)
+ if (!de && errno > 0)
return -errno;
if (!de)
return false;
@@ -367,6 +380,9 @@ static int enable_special_signals(Manager *m) {
assert(m);
+ if (m->test_run)
+ return 0;
+
/* Enable that we get SIGINT on control-alt-del. In containers
* this will fail with EPERM (older) or EINVAL (newer), so
* ignore that. */
@@ -473,7 +489,7 @@ static int manager_setup_signals(Manager *m) {
* later than notify_fd processing, so that the notify
* processing can still figure out to which process/service a
* message belongs, before we reap the process. */
- r = sd_event_source_set_priority(m->signal_event_source, -5);
+ r = sd_event_source_set_priority(m->signal_event_source, SD_EVENT_PRIORITY_NORMAL-5);
if (r < 0)
return r;
@@ -495,6 +511,7 @@ static void manager_clean_environment(Manager *m) {
"MANAGERPID",
"LISTEN_PID",
"LISTEN_FDS",
+ "LISTEN_FDNAMES",
"WATCHDOG_PID",
"WATCHDOG_USEC",
NULL);
@@ -563,6 +580,8 @@ int manager_new(ManagerRunningAs running_as, bool test_run, Manager **_m) {
m->running_as = running_as;
m->exit_code = _MANAGER_EXIT_CODE_INVALID;
m->default_timer_accuracy_usec = USEC_PER_MINUTE;
+ m->default_tasks_accounting = true;
+ m->default_tasks_max = UINT64_C(512);
/* Prepare log fields we can use for structured logging */
m->unit_log_field = unit_log_fields[running_as];
@@ -677,6 +696,8 @@ static int manager_setup_notify(Manager *m) {
if (fd < 0)
return log_error_errno(errno, "Failed to allocate notification socket: %m");
+ fd_inc_rcvbuf(fd, NOTIFY_RCVBUF_SIZE);
+
if (m->running_as == MANAGER_SYSTEM)
m->notify_socket = strdup("/run/systemd/notify");
else {
@@ -718,7 +739,7 @@ static int manager_setup_notify(Manager *m) {
/* Process signals a bit earlier than SIGCHLD, so that we can
* still identify to which service an exit message belongs */
- r = sd_event_source_set_priority(m->notify_event_source, -7);
+ r = sd_event_source_set_priority(m->notify_event_source, SD_EVENT_PRIORITY_NORMAL-7);
if (r < 0)
return log_error_errno(r, "Failed to set priority of notify event source: %m");
@@ -968,7 +989,7 @@ Manager* manager_free(Manager *m) {
free(m->switch_root_init);
for (i = 0; i < _RLIMIT_MAX; i++)
- free(m->rlimit[i]);
+ m->rlimit[i] = mfree(m->rlimit[i]);
assert(hashmap_isempty(m->units_requiring_mounts_for));
hashmap_free(m->units_requiring_mounts_for);
@@ -977,8 +998,7 @@ Manager* manager_free(Manager *m) {
return NULL;
}
-int manager_enumerate(Manager *m) {
- int r = 0;
+void manager_enumerate(Manager *m) {
UnitType c;
assert(m);
@@ -986,8 +1006,6 @@ int manager_enumerate(Manager *m) {
/* Let's ask every type to load all units from disk/kernel
* that it might know */
for (c = 0; c < _UNIT_TYPE_MAX; c++) {
- int q;
-
if (!unit_type_supported(c)) {
log_debug("Unit type .%s is not supported on this system.", unit_type_to_string(c));
continue;
@@ -996,13 +1014,10 @@ int manager_enumerate(Manager *m) {
if (!unit_vtable[c]->enumerate)
continue;
- q = unit_vtable[c]->enumerate(m);
- if (q < 0)
- r = q;
+ unit_vtable[c]->enumerate(m);
}
manager_dispatch_load_queue(m);
- return r;
}
static void manager_coldplug(Manager *m) {
@@ -1084,10 +1099,9 @@ fail:
}
-static int manager_distribute_fds(Manager *m, FDSet *fds) {
- Unit *u;
+static void manager_distribute_fds(Manager *m, FDSet *fds) {
Iterator i;
- int r;
+ Unit *u;
assert(m);
@@ -1096,14 +1110,11 @@ static int manager_distribute_fds(Manager *m, FDSet *fds) {
if (fdset_size(fds) <= 0)
break;
- if (UNIT_VTABLE(u)->distribute_fds) {
- r = UNIT_VTABLE(u)->distribute_fds(u, fds);
- if (r < 0)
- return r;
- }
- }
+ if (!UNIT_VTABLE(u)->distribute_fds)
+ continue;
- return 0;
+ UNIT_VTABLE(u)->distribute_fds(u, fds);
+ }
}
int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
@@ -1136,7 +1147,7 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
/* First, enumerate what we can from all config files */
dual_timestamp_get(&m->units_load_start_timestamp);
- r = manager_enumerate(m);
+ manager_enumerate(m);
dual_timestamp_get(&m->units_load_finish_timestamp);
/* Second, deserialize if there is something to deserialize */
@@ -1147,11 +1158,7 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
* useful to allow container managers to pass some file
* descriptors to us pre-initialized. This enables
* socket-based activation of entire containers. */
- if (fdset_size(fds) > 0) {
- q = manager_distribute_fds(m, fds);
- if (q < 0 && r == 0)
- r = q;
- }
+ manager_distribute_fds(m, fds);
/* We might have deserialized the notify fd, but if we didn't
* then let's create the bus now */
@@ -1181,7 +1188,7 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
return r;
}
-int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool override, sd_bus_error *e, Job **_ret) {
+int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, sd_bus_error *e, Job **_ret) {
int r;
Transaction *tr;
@@ -1204,7 +1211,7 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool ove
if (!tr)
return -ENOMEM;
- r = transaction_add_job_and_dependencies(tr, type, unit, NULL, true, override, false,
+ r = transaction_add_job_and_dependencies(tr, type, unit, NULL, true, false,
mode == JOB_IGNORE_DEPENDENCIES || mode == JOB_IGNORE_REQUIREMENTS,
mode == JOB_IGNORE_DEPENDENCIES, e);
if (r < 0)
@@ -1236,7 +1243,7 @@ tr_abort:
return r;
}
-int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, bool override, sd_bus_error *e, Job **_ret) {
+int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, sd_bus_error *e, Job **ret) {
Unit *unit;
int r;
@@ -1249,7 +1256,23 @@ int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode
if (r < 0)
return r;
- return manager_add_job(m, type, unit, mode, override, e, _ret);
+ return manager_add_job(m, type, unit, mode, e, ret);
+}
+
+int manager_add_job_by_name_and_warn(Manager *m, JobType type, const char *name, JobMode mode, Job **ret) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ int r;
+
+ assert(m);
+ assert(type < _JOB_TYPE_MAX);
+ assert(name);
+ assert(mode < _JOB_MODE_MAX);
+
+ r = manager_add_job_by_name(m, type, name, mode, &error, ret);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to enqueue %s job for %s: %s", job_mode_to_string(mode), name, bus_error_message(&error, r));
+
+ return r;
}
Job *manager_get_job(Manager *m, uint32_t id) {
@@ -1476,7 +1499,7 @@ static unsigned manager_dispatch_dbus_queue(Manager *m) {
return n;
}
-static void manager_invoke_notify_message(Manager *m, Unit *u, pid_t pid, char *buf, size_t n, FDSet *fds) {
+static void manager_invoke_notify_message(Manager *m, Unit *u, pid_t pid, const char *buf, size_t n, FDSet *fds) {
_cleanup_strv_free_ char **tags = NULL;
assert(m);
@@ -1497,9 +1520,33 @@ static void manager_invoke_notify_message(Manager *m, Unit *u, pid_t pid, char *
}
static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
+ _cleanup_fdset_free_ FDSet *fds = NULL;
Manager *m = userdata;
+
+ char buf[NOTIFY_BUFFER_MAX+1];
+ struct iovec iovec = {
+ .iov_base = buf,
+ .iov_len = sizeof(buf)-1,
+ };
+ union {
+ struct cmsghdr cmsghdr;
+ uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) +
+ CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)];
+ } control = {};
+ struct msghdr msghdr = {
+ .msg_iov = &iovec,
+ .msg_iovlen = 1,
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
+ };
+
+ struct cmsghdr *cmsg;
+ struct ucred *ucred = NULL;
+ bool found = false;
+ Unit *u1, *u2, *u3;
+ int r, *fd_array = NULL;
+ unsigned n_fds = 0;
ssize_t n;
- int r;
assert(m);
assert(m->notify_fd == fd);
@@ -1509,106 +1556,80 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
return 0;
}
- for (;;) {
- _cleanup_fdset_free_ FDSet *fds = NULL;
- char buf[NOTIFY_BUFFER_MAX+1];
- struct iovec iovec = {
- .iov_base = buf,
- .iov_len = sizeof(buf)-1,
- };
- union {
- struct cmsghdr cmsghdr;
- uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) +
- CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)];
- } control = {};
- struct msghdr msghdr = {
- .msg_iov = &iovec,
- .msg_iovlen = 1,
- .msg_control = &control,
- .msg_controllen = sizeof(control),
- };
- struct cmsghdr *cmsg;
- struct ucred *ucred = NULL;
- bool found = false;
- Unit *u1, *u2, *u3;
- int *fd_array = NULL;
- unsigned n_fds = 0;
-
- n = recvmsg(m->notify_fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
- if (n < 0) {
- if (errno == EAGAIN || errno == EINTR)
- break;
+ n = recvmsg(m->notify_fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
+ if (n < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
- return -errno;
- }
+ return -errno;
+ }
- CMSG_FOREACH(cmsg, &msghdr) {
- if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
+ CMSG_FOREACH(cmsg, &msghdr) {
+ if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
- fd_array = (int*) CMSG_DATA(cmsg);
- n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
+ fd_array = (int*) CMSG_DATA(cmsg);
+ n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
- } else if (cmsg->cmsg_level == SOL_SOCKET &&
- cmsg->cmsg_type == SCM_CREDENTIALS &&
- cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
+ } else 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);
- }
+ ucred = (struct ucred*) CMSG_DATA(cmsg);
}
+ }
- if (n_fds > 0) {
- assert(fd_array);
+ if (n_fds > 0) {
+ assert(fd_array);
- r = fdset_new_array(&fds, fd_array, n_fds);
- if (r < 0) {
- close_many(fd_array, n_fds);
- return log_oom();
- }
+ r = fdset_new_array(&fds, fd_array, n_fds);
+ if (r < 0) {
+ close_many(fd_array, n_fds);
+ return log_oom();
}
+ }
- if (!ucred || ucred->pid <= 0) {
- log_warning("Received notify message without valid credentials. Ignoring.");
- continue;
- }
+ if (!ucred || ucred->pid <= 0) {
+ log_warning("Received notify message without valid credentials. Ignoring.");
+ return 0;
+ }
- if ((size_t) n >= sizeof(buf)) {
- log_warning("Received notify message exceeded maximum size. Ignoring.");
- continue;
- }
+ if ((size_t) n >= sizeof(buf)) {
+ log_warning("Received notify message exceeded maximum size. Ignoring.");
+ return 0;
+ }
- buf[n] = 0;
+ buf[n] = 0;
- /* Notify every unit that might be interested, but try
- * to avoid notifying the same one multiple times. */
- u1 = manager_get_unit_by_pid_cgroup(m, ucred->pid);
- if (u1) {
- manager_invoke_notify_message(m, u1, ucred->pid, buf, n, fds);
- found = true;
- }
+ /* Notify every unit that might be interested, but try
+ * to avoid notifying the same one multiple times. */
+ u1 = manager_get_unit_by_pid_cgroup(m, ucred->pid);
+ if (u1) {
+ manager_invoke_notify_message(m, u1, ucred->pid, buf, n, fds);
+ found = true;
+ }
- u2 = hashmap_get(m->watch_pids1, PID_TO_PTR(ucred->pid));
- if (u2 && u2 != u1) {
- manager_invoke_notify_message(m, u2, ucred->pid, buf, n, fds);
- found = true;
- }
+ u2 = hashmap_get(m->watch_pids1, PID_TO_PTR(ucred->pid));
+ if (u2 && u2 != u1) {
+ manager_invoke_notify_message(m, u2, ucred->pid, buf, n, fds);
+ found = true;
+ }
- u3 = hashmap_get(m->watch_pids2, PID_TO_PTR(ucred->pid));
- if (u3 && u3 != u2 && u3 != u1) {
- manager_invoke_notify_message(m, u3, ucred->pid, buf, n, fds);
- found = true;
- }
+ u3 = hashmap_get(m->watch_pids2, PID_TO_PTR(ucred->pid));
+ if (u3 && u3 != u2 && u3 != u1) {
+ manager_invoke_notify_message(m, u3, ucred->pid, buf, n, fds);
+ found = true;
+ }
- if (!found)
- log_warning("Cannot find unit for notify message of PID "PID_FMT".", ucred->pid);
+ if (!found)
+ log_warning("Cannot find unit for notify message of PID "PID_FMT".", ucred->pid);
- if (fdset_size(fds) > 0)
- log_warning("Got auxiliary fds with notification message, closing all.");
- }
+ if (fdset_size(fds) > 0)
+ log_warning("Got auxiliary fds with notification message, closing all.");
return 0;
}
-static void invoke_sigchld_event(Manager *m, Unit *u, siginfo_t *si) {
+static void invoke_sigchld_event(Manager *m, Unit *u, const siginfo_t *si) {
assert(m);
assert(u);
assert(si);
@@ -1682,12 +1703,12 @@ static int manager_dispatch_sigchld(Manager *m) {
}
static int manager_start_target(Manager *m, const char *name, JobMode mode) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
log_debug("Activating special unit %s", name);
- r = manager_add_job_by_name(m, JOB_START, name, mode, true, &error, NULL);
+ r = manager_add_job_by_name(m, JOB_START, name, mode, &error, NULL);
if (r < 0)
log_error("Failed to enqueue %s job: %s", name, bus_error_message(&error, r));
@@ -1867,23 +1888,21 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
switch (sfsi.ssi_signo - SIGRTMIN) {
case 20:
- log_debug("Enabling showing of status.");
manager_set_show_status(m, SHOW_STATUS_YES);
break;
case 21:
- log_debug("Disabling showing of status.");
manager_set_show_status(m, SHOW_STATUS_NO);
break;
case 22:
log_set_max_level(LOG_DEBUG);
- log_notice("Setting log level to debug.");
+ log_info("Setting log level to debug.");
break;
case 23:
log_set_max_level(LOG_INFO);
- log_notice("Setting log level to info.");
+ log_info("Setting log level to info.");
break;
case 24:
@@ -1990,8 +2009,7 @@ int manager_loop(Manager *m) {
m->exit_code = MANAGER_OK;
/* Release the path cache */
- set_free_free(m->unit_path_cache);
- m->unit_path_cache = NULL;
+ m->unit_path_cache = set_free_free(m->unit_path_cache);
manager_check_finished(m);
@@ -2011,7 +2029,6 @@ int manager_loop(Manager *m) {
/* Yay, something is going seriously wrong, pause a little */
log_warning("Looping too fast. Throttling execution a little.");
sleep(1);
- continue;
}
if (manager_dispatch_load_queue(m) > 0)
@@ -2101,6 +2118,9 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) {
const char *msg;
int audit_fd, r;
+ if (m->running_as != MANAGER_SYSTEM)
+ return;
+
audit_fd = get_audit_fd();
if (audit_fd < 0)
return;
@@ -2110,9 +2130,6 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) {
if (m->n_reloading > 0)
return;
- if (m->running_as != MANAGER_SYSTEM)
- return;
-
if (u->type != UNIT_SERVICE)
return;
@@ -2542,9 +2559,7 @@ int manager_reload(Manager *m) {
manager_build_unit_path_cache(m);
/* First, enumerate what we can from all config files */
- q = manager_enumerate(m);
- if (q < 0 && r >= 0)
- r = q;
+ manager_enumerate(m);
/* Second, deserialize our stored data */
q = manager_deserialize(m, f, fds);
@@ -2562,6 +2577,10 @@ int manager_reload(Manager *m) {
/* Third, fire things up! */
manager_coldplug(m);
+ /* Sync current state of bus names with our set of listening units */
+ if (m->api_bus)
+ manager_sync_bus_names(m, m->api_bus);
+
assert(m->n_reloading > 0);
m->n_reloading--;
@@ -2761,8 +2780,7 @@ static int create_generator_dir(Manager *m, char **generator, const char *name)
return log_oom();
if (!mkdtemp(p)) {
- log_error_errno(errno, "Failed to create generator directory %s: %m",
- p);
+ log_error_errno(errno, "Failed to create generator directory %s: %m", p);
free(p);
return -errno;
}
@@ -2905,6 +2923,8 @@ int manager_set_default_rlimits(Manager *m, struct rlimit **default_rlimit) {
assert(m);
for (i = 0; i < _RLIMIT_MAX; i++) {
+ m->rlimit[i] = mfree(m->rlimit[i]);
+
if (!default_rlimit[i])
continue;
@@ -2948,12 +2968,15 @@ void manager_set_show_status(Manager *m, ShowStatus mode) {
if (m->running_as != MANAGER_SYSTEM)
return;
+ if (m->show_status != mode)
+ log_debug("%s showing of status.",
+ mode == SHOW_STATUS_NO ? "Disabling" : "Enabling");
m->show_status = mode;
if (mode > 0)
- touch("/run/systemd/show-status");
+ (void) touch("/run/systemd/show-status");
else
- unlink("/run/systemd/show-status");
+ (void) unlink("/run/systemd/show-status");
}
static bool manager_get_show_status(Manager *m, StatusType type) {
@@ -3012,30 +3035,6 @@ void manager_status_printf(Manager *m, StatusType type, const char *status, cons
va_end(ap);
}
-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);
-
- r = unit_name_from_path(path, suffix, &p);
- if (r < 0)
- return r;
-
- found = manager_get_unit(m, p);
- if (!found) {
- *_found = NULL;
- return 0;
- }
-
- *_found = found;
- return 1;
-}
-
Set *manager_get_units_requiring_mounts_for(Manager *m, const char *path) {
char p[strlen(path)+1];
diff --git a/src/core/manager.h b/src/core/manager.h
index cc0e5e3361..f6903a5c34 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -21,14 +21,15 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <libmount.h>
#include <stdbool.h>
#include <stdio.h>
-#include <libmount.h>
#include "sd-bus.h"
#include "sd-event.h"
-#include "fdset.h"
+
#include "cgroup-util.h"
+#include "fdset.h"
#include "hashmap.h"
#include "list.h"
#include "ratelimit.h"
@@ -69,11 +70,11 @@ typedef enum StatusType {
STATUS_TYPE_EMERGENCY,
} StatusType;
+#include "execute.h"
#include "job.h"
#include "path-lookup.h"
-#include "execute.h"
-#include "unit-name.h"
#include "show-status.h"
+#include "unit-name.h"
struct Manager {
/* Note that the set of units we know of is allowed to be
@@ -141,8 +142,6 @@ struct Manager {
sd_event_source *jobs_in_progress_event_source;
- unsigned n_snapshots;
-
LookupPaths lookup_paths;
Set *unit_path_cache;
@@ -262,6 +261,7 @@ struct Manager {
bool default_blockio_accounting;
bool default_tasks_accounting;
+ uint64_t default_tasks_max;
usec_t default_timer_accuracy_usec;
struct rlimit *rlimit[_RLIMIT_MAX];
@@ -316,22 +316,21 @@ struct Manager {
int manager_new(ManagerRunningAs running_as, bool test_run, Manager **m);
Manager* manager_free(Manager *m);
-int manager_enumerate(Manager *m);
+void manager_enumerate(Manager *m);
int manager_startup(Manager *m, FILE *serialization, FDSet *fds);
Job *manager_get_job(Manager *m, uint32_t id);
Unit *manager_get_unit(Manager *m, const char *name);
-int manager_get_unit_by_path(Manager *m, const char *path, const char *suffix, Unit **_found);
-
int manager_get_job_from_dbus_path(Manager *m, const char *s, Job **_j);
int manager_load_unit_prepare(Manager *m, const char *name, const char *path, sd_bus_error *e, Unit **_ret);
int manager_load_unit(Manager *m, const char *name, const char *path, sd_bus_error *e, Unit **_ret);
int manager_load_unit_from_dbus_path(Manager *m, const char *s, sd_bus_error *e, Unit **_u);
-int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool force, sd_bus_error *e, Job **_ret);
-int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, bool force, sd_bus_error *e, Job **_ret);
+int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, sd_bus_error *e, Job **_ret);
+int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, sd_bus_error *e, Job **_ret);
+int manager_add_job_by_name_and_warn(Manager *m, JobType type, const char *name, JobMode mode, Job **ret);
void manager_dump_units(Manager *s, FILE *f, const char *prefix);
void manager_dump_jobs(Manager *s, FILE *f, const char *prefix);
diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c
index 9b16eaa0e2..d73b319c5d 100644
--- a/src/core/mount-setup.c
+++ b/src/core/mount-setup.c
@@ -19,28 +19,31 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/mount.h>
#include <errno.h>
+#include <ftw.h>
#include <stdlib.h>
+#include <sys/mount.h>
#include <unistd.h>
-#include <ftw.h>
-#include "mount-setup.h"
-#include "dev-setup.h"
+#include "alloc-util.h"
#include "bus-util.h"
+#include "cgroup-util.h"
+#include "dev-setup.h"
+#include "efivars.h"
+#include "label.h"
#include "log.h"
#include "macro.h"
-#include "util.h"
-#include "label.h"
-#include "set.h"
-#include "strv.h"
+#include "missing.h"
#include "mkdir.h"
+#include "mount-setup.h"
+#include "mount-util.h"
#include "path-util.h"
-#include "missing.h"
-#include "virt.h"
-#include "efivars.h"
+#include "set.h"
#include "smack-util.h"
-#include "cgroup-util.h"
+#include "strv.h"
+#include "user-util.h"
+#include "util.h"
+#include "virt.h"
typedef enum MountMode {
MNT_NONE = 0,
@@ -301,13 +304,18 @@ int mount_cgroup_controllers(char ***join_controllers) {
return log_oom();
r = symlink(options, t);
- if (r < 0 && errno != EEXIST)
- return log_error_errno(errno, "Failed to create symlink %s: %m", t);
+ if (r >= 0) {
#ifdef SMACK_RUN_LABEL
- r = mac_smack_copy(t, options);
- if (r < 0 && r != -EOPNOTSUPP)
- return log_error_errno(r, "Failed to copy smack label from %s to %s: %m", options, t);
+ _cleanup_free_ char *src;
+ src = strappend("/sys/fs/cgroup/", options);
+ if (!src)
+ return log_oom();
+ r = mac_smack_copy(t, src);
+ if (r < 0 && r != -EOPNOTSUPP)
+ return log_error_errno(r, "Failed to copy smack label from %s to %s: %m", src, t);
#endif
+ } else if (errno != EEXIST)
+ return log_error_errno(errno, "Failed to create symlink %s: %m", t);
}
}
}
diff --git a/src/core/mount.c b/src/core/mount.c
index e7aae6e19a..2ad4ad4f42 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -20,25 +20,33 @@
***/
#include <errno.h>
+#include <signal.h>
#include <stdio.h>
#include <sys/epoll.h>
-#include <signal.h>
-#include "manager.h"
-#include "unit.h"
-#include "mount.h"
-#include "log.h"
#include "sd-messages.h"
-#include "strv.h"
-#include "mkdir.h"
-#include "path-util.h"
-#include "mount-setup.h"
-#include "unit-name.h"
+
+#include "alloc-util.h"
#include "dbus-mount.h"
-#include "special.h"
+#include "escape.h"
#include "exit-status.h"
-#include "fstab-util.h"
#include "formats-util.h"
+#include "fstab-util.h"
+#include "log.h"
+#include "manager.h"
+#include "mkdir.h"
+#include "mount-setup.h"
+#include "mount-util.h"
+#include "mount.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "process-util.h"
+#include "special.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "strv.h"
+#include "unit-name.h"
+#include "unit.h"
#define RETRY_UMOUNT_MAX 32
@@ -246,9 +254,10 @@ static int mount_add_mount_links(Mount *m) {
if (!path_equal(m->where, "/")) {
/* Adds in links to other mount points that might lie further
* up in the hierarchy */
- r = path_get_parent(m->where, &parent);
- if (r < 0)
- return r;
+
+ parent = dirname_malloc(m->where);
+ if (!parent)
+ return -ENOMEM;
r = unit_require_mounts_for(UNIT(m), parent);
if (r < 0)
@@ -326,7 +335,7 @@ static int mount_add_device_links(Mount *m) {
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);
+ r = unit_add_node_link(UNIT(m), p->what, device_wants_mount, m->from_fragment ? UNIT_BINDS_TO : UNIT_REQUIRES);
if (r < 0)
return r;
@@ -376,12 +385,15 @@ static bool should_umount(Mount *m) {
}
static int mount_add_default_dependencies(Mount *m) {
- const char *after, *after2, *online;
MountParameters *p;
+ const char *after;
int r;
assert(m);
+ if (!UNIT(m)->default_dependencies)
+ return 0;
+
if (UNIT(m)->manager->running_as != MANAGER_SYSTEM)
return 0;
@@ -402,30 +414,34 @@ static int mount_add_default_dependencies(Mount *m) {
return 0;
if (mount_is_network(p)) {
- after = SPECIAL_REMOTE_FS_PRE_TARGET;
- after2 = SPECIAL_NETWORK_TARGET;
- online = SPECIAL_NETWORK_ONLINE_TARGET;
- } else {
- after = SPECIAL_LOCAL_FS_PRE_TARGET;
- after2 = NULL;
- online = NULL;
- }
-
- r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after, NULL, true);
- if (r < 0)
- return r;
+ /* We order ourselves after network.target. This is
+ * primarily useful at shutdown: services that take
+ * down the network should order themselves before
+ * network.target, so that they are shut down only
+ * after this mount unit is stopped. */
- if (after2) {
- r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after2, NULL, true);
+ r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, SPECIAL_NETWORK_TARGET, NULL, true);
if (r < 0)
return r;
- }
- if (online) {
- r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_WANTS, UNIT_AFTER, online, NULL, true);
+ /* We pull in network-online.target, and order
+ * ourselves after it. This is useful at start-up to
+ * actively pull in tools that want to be started
+ * before we start mounting network file systems, and
+ * whose purpose it is to delay this until the network
+ * is "up". */
+
+ r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_WANTS, UNIT_AFTER, SPECIAL_NETWORK_ONLINE_TARGET, NULL, true);
if (r < 0)
return r;
- }
+
+ after = SPECIAL_REMOTE_FS_PRE_TARGET;
+ } else
+ after = SPECIAL_LOCAL_FS_PRE_TARGET;
+
+ r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after, NULL, true);
+ if (r < 0)
+ return r;
if (should_umount(m)) {
r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true);
@@ -522,11 +538,9 @@ static int mount_add_extras(Mount *m) {
if (r < 0)
return r;
- if (u->default_dependencies) {
- r = mount_add_default_dependencies(m);
- if (r < 0)
- return r;
- }
+ r = mount_add_default_dependencies(m);
+ if (r < 0)
+ return r;
return 0;
}
@@ -621,19 +635,19 @@ static int mount_coldplug(Unit *u) {
if (new_state == m->state)
return 0;
- if (new_state == MOUNT_MOUNTING ||
- new_state == MOUNT_MOUNTING_DONE ||
- new_state == MOUNT_REMOUNTING ||
- new_state == MOUNT_UNMOUNTING ||
- new_state == MOUNT_MOUNTING_SIGTERM ||
- new_state == MOUNT_MOUNTING_SIGKILL ||
- new_state == MOUNT_UNMOUNTING_SIGTERM ||
- new_state == MOUNT_UNMOUNTING_SIGKILL ||
- new_state == MOUNT_REMOUNTING_SIGTERM ||
- new_state == MOUNT_REMOUNTING_SIGKILL) {
-
- if (m->control_pid <= 0)
- return -EBADMSG;
+ if (m->control_pid > 0 &&
+ pid_is_unwaited(m->control_pid) &&
+ IN_SET(new_state,
+ MOUNT_MOUNTING,
+ MOUNT_MOUNTING_DONE,
+ MOUNT_REMOUNTING,
+ MOUNT_UNMOUNTING,
+ MOUNT_MOUNTING_SIGTERM,
+ MOUNT_MOUNTING_SIGKILL,
+ MOUNT_UNMOUNTING_SIGTERM,
+ MOUNT_UNMOUNTING_SIGKILL,
+ MOUNT_REMOUNTING_SIGTERM,
+ MOUNT_REMOUNTING_SIGKILL)) {
r = unit_watch_pid(UNIT(m), m->control_pid);
if (r < 0)
@@ -694,6 +708,9 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
.apply_chroot = true,
.apply_tty_stdin = true,
.bus_endpoint_fd = -1,
+ .stdin_fd = -1,
+ .stdout_fd = -1,
+ .stderr_fd = -1,
};
assert(m);
@@ -849,6 +866,11 @@ fail:
mount_enter_mounted(m, MOUNT_FAILURE_RESOURCES);
}
+static int mount_get_opts(Mount *m, char **ret) {
+ return fstab_filter_options(m->parameters_fragment.options,
+ "nofail\0" "noauto\0" "auto\0", NULL, NULL, ret);
+}
+
static void mount_enter_mounting(Mount *m) {
int r;
MountParameters *p;
@@ -874,8 +896,7 @@ static void mount_enter_mounting(Mount *m) {
if (m->from_fragment) {
_cleanup_free_ char *opts = NULL;
- r = fstab_filter_options(m->parameters_fragment.options,
- "nofail\0" "noauto\0" "auto\0", NULL, NULL, &opts);
+ r = mount_get_opts(m, &opts);
if (r < 0)
goto fail;
@@ -1556,7 +1577,7 @@ static int mount_get_timeout(Unit *u, uint64_t *timeout) {
return 1;
}
-static int mount_enumerate(Manager *m) {
+static void mount_enumerate(Manager *m) {
int r;
assert(m);
@@ -1568,29 +1589,40 @@ static int mount_enumerate(Manager *m) {
m->mount_monitor = mnt_new_monitor();
if (!m->mount_monitor) {
- r = -ENOMEM;
+ log_oom();
goto fail;
}
r = mnt_monitor_enable_kernel(m->mount_monitor, 1);
- if (r < 0)
+ if (r < 0) {
+ log_error_errno(r, "Failed to enable watching of kernel mount events: %m");
goto fail;
+ }
+
r = mnt_monitor_enable_userspace(m->mount_monitor, 1, NULL);
- if (r < 0)
+ if (r < 0) {
+ log_error_errno(r, "Failed to enable watching of userspace mount events: %m");
goto fail;
+ }
/* mnt_unref_monitor() will close the fd */
fd = r = mnt_monitor_get_fd(m->mount_monitor);
- if (r < 0)
+ if (r < 0) {
+ log_error_errno(r, "Failed to acquire watch file descriptor: %m");
goto fail;
+ }
r = sd_event_add_io(m->event, &m->mount_event_source, fd, EPOLLIN, mount_dispatch_io, m);
- if (r < 0)
+ if (r < 0) {
+ log_error_errno(r, "Failed to watch mount file descriptor: %m");
goto fail;
+ }
r = sd_event_source_set_priority(m->mount_event_source, -10);
- if (r < 0)
+ if (r < 0) {
+ log_error_errno(r, "Failed to adjust mount watch priority: %m");
goto fail;
+ }
(void) sd_event_source_set_description(m->mount_event_source, "mount-monitor-dispatch");
}
@@ -1599,11 +1631,10 @@ static int mount_enumerate(Manager *m) {
if (r < 0)
goto fail;
- return 0;
+ return;
fail:
mount_shutdown(m);
- return r;
}
static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
@@ -1756,24 +1787,6 @@ static int mount_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) {
return unit_kill_common(u, who, signo, -1, MOUNT(u)->control_pid, error);
}
-static const char* const mount_state_table[_MOUNT_STATE_MAX] = {
- [MOUNT_DEAD] = "dead",
- [MOUNT_MOUNTING] = "mounting",
- [MOUNT_MOUNTING_DONE] = "mounting-done",
- [MOUNT_MOUNTED] = "mounted",
- [MOUNT_REMOUNTING] = "remounting",
- [MOUNT_UNMOUNTING] = "unmounting",
- [MOUNT_MOUNTING_SIGTERM] = "mounting-sigterm",
- [MOUNT_MOUNTING_SIGKILL] = "mounting-sigkill",
- [MOUNT_REMOUNTING_SIGTERM] = "remounting-sigterm",
- [MOUNT_REMOUNTING_SIGKILL] = "remounting-sigkill",
- [MOUNT_UNMOUNTING_SIGTERM] = "unmounting-sigterm",
- [MOUNT_UNMOUNTING_SIGKILL] = "unmounting-sigkill",
- [MOUNT_FAILED] = "failed"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(mount_state, MountState);
-
static const char* const mount_exec_command_table[_MOUNT_EXEC_COMMAND_MAX] = {
[MOUNT_EXEC_MOUNT] = "ExecMount",
[MOUNT_EXEC_UNMOUNT] = "ExecUnmount",
diff --git a/src/core/mount.h b/src/core/mount.h
index 280ea0d638..9f78aa9075 100644
--- a/src/core/mount.h
+++ b/src/core/mount.h
@@ -23,26 +23,8 @@
typedef struct Mount Mount;
-#include "kill.h"
#include "execute.h"
-
-typedef enum MountState {
- MOUNT_DEAD,
- 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,
- MOUNT_MOUNTING_SIGTERM,
- MOUNT_MOUNTING_SIGKILL,
- MOUNT_REMOUNTING_SIGTERM,
- MOUNT_REMOUNTING_SIGKILL,
- MOUNT_UNMOUNTING_SIGTERM,
- MOUNT_UNMOUNTING_SIGKILL,
- MOUNT_FAILED,
- _MOUNT_STATE_MAX,
- _MOUNT_STATE_INVALID = -1
-} MountState;
+#include "kill.h"
typedef enum MountExecCommand {
MOUNT_EXEC_MOUNT,
@@ -120,9 +102,6 @@ extern const UnitVTable mount_vtable;
void mount_fd_event(Manager *m, int events);
-const char* mount_state_to_string(MountState i) _const_;
-MountState mount_state_from_string(const char *s) _pure_;
-
const char* mount_exec_command_to_string(MountExecCommand i) _const_;
MountExecCommand mount_exec_command_from_string(const char *s) _pure_;
diff --git a/src/core/namespace.c b/src/core/namespace.c
index eb88574f8f..81ba09ea5d 100644
--- a/src/core/namespace.c
+++ b/src/core/namespace.c
@@ -20,23 +20,31 @@
***/
#include <errno.h>
-#include <sys/mount.h>
-#include <string.h>
+#include <sched.h>
#include <stdio.h>
-#include <unistd.h>
+#include <string.h>
+#include <sys/mount.h>
#include <sys/stat.h>
-#include <sched.h>
+#include <unistd.h>
#include <linux/fs.h>
-#include "strv.h"
-#include "util.h"
-#include "path-util.h"
-#include "missing.h"
-#include "loopback-setup.h"
+#include "alloc-util.h"
#include "dev-setup.h"
-#include "selinux-util.h"
-#include "namespace.h"
+#include "fd-util.h"
+#include "loopback-setup.h"
+#include "missing.h"
#include "mkdir.h"
+#include "mount-util.h"
+#include "namespace.h"
+#include "path-util.h"
+#include "selinux-util.h"
+#include "socket-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "strv.h"
+#include "umask-util.h"
+#include "user-util.h"
+#include "util.h"
typedef enum MountMode {
/* This is ordered by priority! */
@@ -643,16 +651,7 @@ int setup_tmp_dirs(const char *id, char **tmp_dir, char **var_tmp_dir) {
int setup_netns(int netns_storage_socket[2]) {
_cleanup_close_ int netns = -1;
- union {
- struct cmsghdr cmsghdr;
- uint8_t buf[CMSG_SPACE(sizeof(int))];
- } control = {};
- struct msghdr mh = {
- .msg_control = &control,
- .msg_controllen = sizeof(control),
- };
- struct cmsghdr *cmsg;
- int r;
+ int r, q;
assert(netns_storage_socket);
assert(netns_storage_socket[0] >= 0);
@@ -669,12 +668,8 @@ int setup_netns(int netns_storage_socket[2]) {
if (lockf(netns_storage_socket[0], F_LOCK, 0) < 0)
return -errno;
- if (recvmsg(netns_storage_socket[0], &mh, MSG_DONTWAIT|MSG_CMSG_CLOEXEC) < 0) {
- if (errno != EAGAIN) {
- r = -errno;
- goto fail;
- }
-
+ netns = receive_one_fd(netns_storage_socket[0], MSG_DONTWAIT);
+ if (netns == -EAGAIN) {
/* Nothing stored yet, so let's create a new namespace */
if (unshare(CLONE_NEWNET) < 0) {
@@ -691,15 +686,13 @@ int setup_netns(int netns_storage_socket[2]) {
}
r = 1;
- } else {
- /* Yay, found something, so let's join the namespace */
- CMSG_FOREACH(cmsg, &mh)
- if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
- assert(cmsg->cmsg_len == CMSG_LEN(sizeof(int)));
- netns = *(int*) CMSG_DATA(cmsg);
- }
+ } else if (netns < 0) {
+ r = netns;
+ goto fail;
+ } else {
+ /* Yay, found something, so let's join the namespace */
if (setns(netns, CLONE_NEWNET) < 0) {
r = -errno;
goto fail;
@@ -708,21 +701,14 @@ int setup_netns(int netns_storage_socket[2]) {
r = 0;
}
- cmsg = CMSG_FIRSTHDR(&mh);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(sizeof(int));
- memcpy(CMSG_DATA(cmsg), &netns, sizeof(int));
- mh.msg_controllen = cmsg->cmsg_len;
-
- if (sendmsg(netns_storage_socket[1], &mh, MSG_DONTWAIT|MSG_NOSIGNAL) < 0) {
- r = -errno;
+ q = send_one_fd(netns_storage_socket[1], netns, MSG_DONTWAIT);
+ if (q < 0) {
+ r = q;
goto fail;
}
fail:
lockf(netns_storage_socket[0], F_ULOCK, 0);
-
return r;
}
diff --git a/src/core/path.c b/src/core/path.c
index e9111d0612..e2d39eaa65 100644
--- a/src/core/path.c
+++ b/src/core/path.c
@@ -19,20 +19,26 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/inotify.h>
-#include <sys/epoll.h>
#include <errno.h>
+#include <sys/epoll.h>
+#include <sys/inotify.h>
#include <unistd.h>
-#include "unit.h"
-#include "unit-name.h"
-#include "path.h"
-#include "mkdir.h"
+#include "bus-error.h"
+#include "bus-util.h"
#include "dbus-path.h"
-#include "special.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "glob-util.h"
#include "macro.h"
-#include "bus-util.h"
-#include "bus-error.h"
+#include "mkdir.h"
+#include "path.h"
+#include "special.h"
+#include "stat-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "unit-name.h"
+#include "unit.h"
static const UnitActiveState state_translation_table[_PATH_STATE_MAX] = {
[PATH_DEAD] = UNIT_INACTIVE,
@@ -309,20 +315,20 @@ static int path_add_default_dependencies(Path *p) {
assert(p);
- r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE,
- SPECIAL_PATHS_TARGET, NULL, true);
+ if (!UNIT(p)->default_dependencies)
+ return 0;
+
+ r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE, SPECIAL_PATHS_TARGET, NULL, true);
if (r < 0)
return r;
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);
+ r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
if (r < 0)
return r;
}
- return unit_add_two_dependencies_by_name(UNIT(p), UNIT_BEFORE, UNIT_CONFLICTS,
- SPECIAL_SHUTDOWN_TARGET, NULL, true);
+ return unit_add_two_dependencies_by_name(UNIT(p), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
}
static int path_load(Unit *u) {
@@ -354,11 +360,9 @@ static int path_load(Unit *u) {
if (r < 0)
return r;
- if (UNIT(p)->default_dependencies) {
- r = path_add_default_dependencies(p);
- if (r < 0)
- return r;
- }
+ r = path_add_default_dependencies(p);
+ if (r < 0)
+ return r;
}
return path_verify(p);
@@ -461,7 +465,7 @@ static void path_enter_dead(Path *p, PathResult f) {
}
static void path_enter_running(Path *p) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert(p);
@@ -470,8 +474,7 @@ static void path_enter_running(Path *p) {
if (unit_stop_pending(UNIT(p)))
return;
- r = manager_add_job(UNIT(p)->manager, JOB_START, UNIT_TRIGGER(UNIT(p)),
- JOB_REPLACE, true, &error, NULL);
+ r = manager_add_job(UNIT(p)->manager, JOB_START, UNIT_TRIGGER(UNIT(p)), JOB_REPLACE, &error, NULL);
if (r < 0)
goto fail;
@@ -715,15 +718,6 @@ static void path_reset_failed(Unit *u) {
p->result = PATH_SUCCESS;
}
-static const char* const path_state_table[_PATH_STATE_MAX] = {
- [PATH_DEAD] = "dead",
- [PATH_WAITING] = "waiting",
- [PATH_RUNNING] = "running",
- [PATH_FAILED] = "failed"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(path_state, PathState);
-
static const char* const path_type_table[_PATH_TYPE_MAX] = {
[PATH_EXISTS] = "PathExists",
[PATH_EXISTS_GLOB] = "PathExistsGlob",
diff --git a/src/core/path.h b/src/core/path.h
index dec39333e4..deb9bab1e5 100644
--- a/src/core/path.h
+++ b/src/core/path.h
@@ -26,15 +26,6 @@ typedef struct PathSpec PathSpec;
#include "unit.h"
-typedef enum PathState {
- PATH_DEAD,
- PATH_WAITING,
- PATH_RUNNING,
- PATH_FAILED,
- _PATH_STATE_MAX,
- _PATH_STATE_INVALID = -1
-} PathState;
-
typedef enum PathType {
PATH_EXISTS,
PATH_EXISTS_GLOB,
@@ -96,9 +87,6 @@ void path_free_specs(Path *p);
extern const UnitVTable path_vtable;
-const char* path_state_to_string(PathState i) _const_;
-PathState path_state_from_string(const char *s) _pure_;
-
const char* path_type_to_string(PathType i) _const_;
PathType path_type_from_string(const char *s) _pure_;
diff --git a/src/core/scope.c b/src/core/scope.c
index 98395becfd..1953af1f88 100644
--- a/src/core/scope.c
+++ b/src/core/scope.c
@@ -22,14 +22,17 @@
#include <errno.h>
#include <unistd.h>
+#include "alloc-util.h"
+#include "dbus-scope.h"
+#include "load-dropin.h"
#include "log.h"
-#include "strv.h"
+#include "scope.h"
#include "special.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "strv.h"
#include "unit-name.h"
#include "unit.h"
-#include "scope.h"
-#include "dbus-scope.h"
-#include "load-dropin.h"
static const UnitActiveState state_translation_table[_SCOPE_STATE_MAX] = {
[SCOPE_DEAD] = UNIT_INACTIVE,
@@ -51,7 +54,6 @@ static void scope_init(Unit *u) {
s->timeout_stop_usec = u->manager->default_timeout_stop_usec;
UNIT(s)->ignore_on_isolate = true;
- UNIT(s)->ignore_on_snapshot = true;
}
static void scope_done(Unit *u) {
@@ -120,6 +122,9 @@ static int scope_add_default_dependencies(Scope *s) {
assert(s);
+ if (!UNIT(s)->default_dependencies)
+ return 0;
+
/* Make sure scopes are unloaded on shutdown */
r = unit_add_two_dependencies_by_name(
UNIT(s),
@@ -171,11 +176,9 @@ static int scope_load(Unit *u) {
if (r < 0)
return r;
- if (u->default_dependencies) {
- r = scope_add_default_dependencies(s);
- if (r < 0)
- return r;
- }
+ r = scope_add_default_dependencies(s);
+ if (r < 0)
+ return r;
return scope_verify(s);
}
@@ -399,15 +402,10 @@ static bool scope_check_gc(Unit *u) {
/* Never clean up scopes that still have a process around,
* even if the scope is formally dead. */
- if (u->cgroup_path) {
- int r;
-
- r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path);
- if (r <= 0)
- return true;
- }
+ if (!u->cgroup_path)
+ return false;
- return false;
+ return cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path) <= 0;
}
static void scope_notify_cgroup_empty_event(Unit *u) {
@@ -507,7 +505,7 @@ _pure_ static const char *scope_sub_state_to_string(Unit *u) {
return scope_state_to_string(SCOPE(u)->state);
}
-static int scope_enumerate(Manager *m) {
+static void scope_enumerate(Manager *m) {
Unit *u;
int r;
@@ -521,19 +519,25 @@ static int scope_enumerate(Manager *m) {
u = manager_get_unit(m, SPECIAL_INIT_SCOPE);
if (!u) {
u = unit_new(m, sizeof(Scope));
- if (!u)
- return log_oom();
+ if (!u) {
+ log_oom();
+ return;
+ }
r = unit_add_name(u, SPECIAL_INIT_SCOPE);
if (r < 0) {
unit_free(u);
- return log_error_errno(r, "Failed to add init.scope name");
+ log_error_errno(r, "Failed to add init.scope name");
+ return;
}
}
u->transient = true;
u->default_dependencies = false;
u->no_gc = true;
+ u->ignore_on_isolate = true;
+ u->refuse_manual_start = true;
+ u->refuse_manual_stop = true;
SCOPE(u)->deserialized_state = SCOPE_RUNNING;
SCOPE(u)->kill_context.kill_signal = SIGRTMIN+14;
@@ -545,21 +549,8 @@ static int scope_enumerate(Manager *m) {
unit_add_to_load_queue(u);
unit_add_to_dbus_queue(u);
-
- return 0;
}
-static const char* const scope_state_table[_SCOPE_STATE_MAX] = {
- [SCOPE_DEAD] = "dead",
- [SCOPE_RUNNING] = "running",
- [SCOPE_ABANDONED] = "abandoned",
- [SCOPE_STOP_SIGTERM] = "stop-sigterm",
- [SCOPE_STOP_SIGKILL] = "stop-sigkill",
- [SCOPE_FAILED] = "failed",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(scope_state, ScopeState);
-
static const char* const scope_result_table[_SCOPE_RESULT_MAX] = {
[SCOPE_SUCCESS] = "success",
[SCOPE_FAILURE_RESOURCES] = "resources",
@@ -581,6 +572,7 @@ const UnitVTable scope_vtable = {
.no_alias = true,
.no_instances = true,
+ .can_transient = true,
.init = scope_init,
.load = scope_load,
@@ -615,7 +607,5 @@ const UnitVTable scope_vtable = {
.bus_set_property = bus_scope_set_property,
.bus_commit_properties = bus_scope_commit_properties,
- .can_transient = true,
-
.enumerate = scope_enumerate,
};
diff --git a/src/core/scope.h b/src/core/scope.h
index 4452fe2c94..f838ee5357 100644
--- a/src/core/scope.h
+++ b/src/core/scope.h
@@ -25,17 +25,6 @@ typedef struct Scope Scope;
#include "kill.h"
-typedef enum ScopeState {
- SCOPE_DEAD,
- SCOPE_RUNNING,
- SCOPE_ABANDONED,
- SCOPE_STOP_SIGTERM,
- SCOPE_STOP_SIGKILL,
- SCOPE_FAILED,
- _SCOPE_STATE_MAX,
- _SCOPE_STATE_INVALID = -1
-} ScopeState;
-
typedef enum ScopeResult {
SCOPE_SUCCESS,
SCOPE_FAILURE_RESOURCES,
@@ -64,8 +53,5 @@ extern const UnitVTable scope_vtable;
int scope_abandon(Scope *s);
-const char* scope_state_to_string(ScopeState i) _const_;
-ScopeState scope_state_from_string(const char *s) _pure_;
-
const char* scope_result_to_string(ScopeResult i) _const_;
ScopeResult scope_result_from_string(const char *s) _pure_;
diff --git a/src/core/selinux-access.c b/src/core/selinux-access.c
index 40ca0c6166..3f3c5bf9fc 100644
--- a/src/core/selinux-access.c
+++ b/src/core/selinux-access.c
@@ -23,22 +23,25 @@
#ifdef HAVE_SELINUX
-#include <stdio.h>
#include <errno.h>
-#include <selinux/selinux.h>
#include <selinux/avc.h>
+#include <selinux/selinux.h>
+#include <stdio.h>
#ifdef HAVE_AUDIT
#include <libaudit.h>
#endif
#include "sd-bus.h"
+
+#include "alloc-util.h"
+#include "audit-fd.h"
#include "bus-util.h"
-#include "util.h"
#include "log.h"
+#include "path-util.h"
#include "selinux-util.h"
-#include "audit-fd.h"
+#include "stdio-util.h"
#include "strv.h"
-#include "path-util.h"
+#include "util.h"
static bool initialized = false;
@@ -131,62 +134,44 @@ _printf_(2, 3) static int log_callback(int type, const char *fmt, ...) {
#endif
va_start(ap, fmt);
- log_internalv(LOG_AUTH | callback_type_to_priority(type),
- 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;
}
-/*
- Function must be called once to initialize the SELinux AVC environment.
- Sets up callbacks.
- If you want to cleanup memory you should need to call selinux_access_finish.
-*/
-static int access_init(void) {
- int r = 0;
-
- if (avc_open(NULL, 0))
- return log_error_errno(errno, "avc_open() failed: %m");
+static int access_init(sd_bus_error *error) {
- selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback) audit_callback);
- selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) log_callback);
-
- if (security_getenforce() < 0){
- r = -errno;
- avc_destroy();
- }
-
- return r;
-}
-
-static int mac_selinux_access_init(sd_bus_error *error) {
- int r;
+ if (!mac_selinux_use())
+ return 0;
if (initialized)
- return 0;
+ return 1;
- if (!mac_selinux_use())
- return 0;
+ if (avc_open(NULL, 0) != 0) {
+ int enforce, saved_errno = errno;
- r = access_init();
- if (r < 0)
- return sd_bus_error_set(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to initialize SELinux.");
+ enforce = security_getenforce();
+ log_full_errno(enforce != 0 ? LOG_ERR : LOG_WARNING, saved_errno, "Failed to open the SELinux AVC: %m");
- initialized = true;
- return 0;
-}
-#endif
+ /* If enforcement isn't on, then let's suppress this
+ * error, and just don't do any AVC checks. The
+ * warning we printed is hence all the admin will
+ * see. */
+ if (enforce == 0)
+ return 0;
-void mac_selinux_access_free(void) {
+ /* Return an access denied error, if we couldn't load
+ * the AVC but enforcing mode was on, or we couldn't
+ * determine whether it is one. */
+ return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to open the SELinux AVC: %s", strerror(saved_errno));
+ }
-#ifdef HAVE_SELINUX
- if (!initialized)
- return;
+ selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback) audit_callback);
+ selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) log_callback);
- avc_destroy();
- initialized = false;
-#endif
+ initialized = true;
+ return 1;
}
/*
@@ -201,8 +186,7 @@ int mac_selinux_generic_access_check(
const char *permission,
sd_bus_error *error) {
-#ifdef HAVE_SELINUX
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
const char *tclass = NULL, *scon = NULL;
struct audit_info audit_info = {};
_cleanup_free_ char *cl = NULL;
@@ -214,11 +198,8 @@ int mac_selinux_generic_access_check(
assert(permission);
assert(error);
- if (!mac_selinux_use())
- return 0;
-
- r = mac_selinux_access_init(error);
- if (r < 0)
+ r = access_init(error);
+ if (r <= 0)
return r;
r = sd_bus_query_sender_creds(
@@ -285,7 +266,17 @@ finish:
}
return r;
+}
+
#else
+
+int mac_selinux_generic_access_check(
+ sd_bus_message *message,
+ const char *path,
+ const char *permission,
+ sd_bus_error *error) {
+
return 0;
-#endif
}
+
+#endif
diff --git a/src/core/selinux-access.h b/src/core/selinux-access.h
index e6b4dd7fee..3566ba529f 100644
--- a/src/core/selinux-access.h
+++ b/src/core/selinux-access.h
@@ -22,11 +22,10 @@
***/
#include "sd-bus.h"
+
#include "bus-util.h"
#include "manager.h"
-void mac_selinux_access_free(void);
-
int mac_selinux_generic_access_check(sd_bus_message *message, const char *path, const char *permission, sd_bus_error *error);
#ifdef HAVE_SELINUX
diff --git a/src/core/selinux-setup.c b/src/core/selinux-setup.c
index e5b457643b..d4757e0853 100644
--- a/src/core/selinux-setup.c
+++ b/src/core/selinux-setup.c
@@ -19,19 +19,20 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
-#include <stdio.h>
#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
#ifdef HAVE_SELINUX
#include <selinux/selinux.h>
#endif
+#include "log.h"
+#include "macro.h"
#include "selinux-setup.h"
#include "selinux-util.h"
-#include "macro.h"
+#include "string-util.h"
#include "util.h"
-#include "log.h"
#ifdef HAVE_SELINUX
_printf_(2,3)
@@ -78,14 +79,14 @@ int mac_selinux_setup(bool *loaded_policy) {
before_load = now(CLOCK_MONOTONIC);
r = selinux_init_load_policy(&enforce);
if (r == 0) {
+ _cleanup_(mac_selinux_freep) char *label = NULL;
char timespan[FORMAT_TIMESPAN_MAX];
- char *label;
mac_selinux_retest();
/* Transition to the new context */
r = mac_selinux_get_create_label_from_exe(SYSTEMD_BINARY_PATH, &label);
- if (r < 0 || label == NULL) {
+ if (r < 0 || !label) {
log_open();
log_error("Failed to compute init label, ignoring.");
} else {
@@ -94,8 +95,6 @@ int mac_selinux_setup(bool *loaded_policy) {
log_open();
if (r < 0)
log_error("Failed to transition into init label '%s', ignoring.", label);
-
- mac_selinux_free(label);
}
after_load = now(CLOCK_MONOTONIC);
diff --git a/src/core/service.c b/src/core/service.c
index f7de5e89ff..c5b689a35c 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -23,31 +23,38 @@
#include <signal.h>
#include <unistd.h>
+#include "alloc-util.h"
#include "async.h"
-#include "manager.h"
-#include "unit.h"
-#include "service.h"
-#include "load-fragment.h"
-#include "load-dropin.h"
-#include "log.h"
-#include "strv.h"
-#include "unit-name.h"
-#include "unit-printf.h"
+#include "bus-error.h"
+#include "bus-kernel.h"
+#include "bus-util.h"
#include "dbus-service.h"
-#include "special.h"
-#include "exit-status.h"
#include "def.h"
-#include "path-util.h"
-#include "util.h"
-#include "utf8.h"
#include "env-util.h"
+#include "escape.h"
+#include "exit-status.h"
+#include "fd-util.h"
#include "fileio.h"
-#include "bus-error.h"
-#include "bus-util.h"
-#include "bus-kernel.h"
#include "formats-util.h"
+#include "fs-util.h"
+#include "load-dropin.h"
+#include "load-fragment.h"
+#include "log.h"
+#include "manager.h"
+#include "parse-util.h"
+#include "path-util.h"
#include "process-util.h"
+#include "service.h"
#include "signal-util.h"
+#include "special.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "strv.h"
+#include "unit-name.h"
+#include "unit-printf.h"
+#include "unit.h"
+#include "utf8.h"
+#include "util.h"
static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
[SERVICE_DEAD] = UNIT_INACTIVE,
@@ -108,6 +115,7 @@ static void service_init(Unit *u) {
s->type = _SERVICE_TYPE_INVALID;
s->socket_fd = -1;
s->bus_endpoint_fd = -1;
+ s->stdin_fd = s->stdout_fd = s->stderr_fd = -1;
s->guess_main_pid = true;
RATELIMIT_INIT(s->start_limit, u->manager->default_start_limit_interval, u->manager->default_start_limit_burst);
@@ -167,7 +175,7 @@ static int service_set_main_pid(Service *s, pid_t pid) {
s->main_pid = pid;
s->main_pid_known = true;
- if (get_parent_of_pid(pid, &ppid) >= 0 && ppid != getpid()) {
+ if (get_process_ppid(pid, &ppid) >= 0 && ppid != getpid()) {
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
@@ -261,6 +269,7 @@ static void service_fd_store_unlink(ServiceFDStore *fs) {
sd_event_source_unref(fs->event_source);
}
+ free(fs->fdname);
safe_close(fs->fd);
free(fs);
}
@@ -270,11 +279,15 @@ static void service_release_resources(Unit *u) {
assert(s);
- if (!s->fd_store)
+ if (!s->fd_store && s->stdin_fd < 0 && s->stdout_fd < 0 && s->stderr_fd < 0)
return;
log_unit_debug(u, "Releasing all resources.");
+ s->stdin_fd = safe_close(s->stdin_fd);
+ s->stdout_fd = safe_close(s->stdout_fd);
+ s->stderr_fd = safe_close(s->stderr_fd);
+
while (s->fd_store)
service_fd_store_unlink(s->fd_store);
@@ -310,6 +323,8 @@ static void service_done(Unit *u) {
s->bus_name = mfree(s->bus_name);
}
+ s->bus_name_owner = mfree(s->bus_name_owner);
+
s->bus_endpoint_fd = safe_close(s->bus_endpoint_fd);
service_close_socket_fd(s);
service_connection_unref(s);
@@ -334,7 +349,7 @@ static int on_fd_store_io(sd_event_source *e, int fd, uint32_t revents, void *us
return 0;
}
-static int service_add_fd_store(Service *s, int fd) {
+static int service_add_fd_store(Service *s, int fd, const char *name) {
ServiceFDStore *fs;
int r;
@@ -361,9 +376,15 @@ static int service_add_fd_store(Service *s, int fd) {
fs->fd = fd;
fs->service = s;
+ fs->fdname = strdup(name ?: "stored");
+ if (!fs->fdname) {
+ free(fs);
+ return -ENOMEM;
+ }
r = sd_event_add_io(UNIT(s)->manager->event, &fs->event_source, fd, 0, on_fd_store_io, fs);
if (r < 0) {
+ free(fs->fdname);
free(fs);
return r;
}
@@ -376,7 +397,7 @@ static int service_add_fd_store(Service *s, int fd) {
return 1;
}
-static int service_add_fd_store_set(Service *s, FDSet *fds) {
+static int service_add_fd_store_set(Service *s, FDSet *fds, const char *name) {
int r;
assert(s);
@@ -391,7 +412,7 @@ static int service_add_fd_store_set(Service *s, FDSet *fds) {
if (fd < 0)
break;
- r = service_add_fd_store(s, fd);
+ r = service_add_fd_store(s, fd, name);
if (r < 0)
return log_unit_error_errno(UNIT(s), r, "Couldn't add fd to fd store: %m");
if (r > 0) {
@@ -401,7 +422,7 @@ static int service_add_fd_store_set(Service *s, FDSet *fds) {
}
if (fdset_size(fds) > 0)
- log_unit_warning(UNIT(s), "Tried to store more fds than FDStoreMax=%u allows, closing remaining.", s->n_fd_store_max);
+ log_unit_warning(UNIT(s), "Tried to store more fds than FileDescriptorStoreMax=%u allows, closing remaining.", s->n_fd_store_max);
return 0;
}
@@ -496,15 +517,38 @@ static int service_add_default_dependencies(Service *s) {
assert(s);
+ if (!UNIT(s)->default_dependencies)
+ return 0;
+
/* Add a number of automatic dependencies useful for the
* majority of services. */
- /* First, pull in base system */
- r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_BASIC_TARGET, NULL, true);
+ if (UNIT(s)->manager->running_as == MANAGER_SYSTEM) {
+ /* First, pull in the really early boot stuff, and
+ * require it, so that we fail if we can't acquire
+ * it. */
+
+ r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
+ if (r < 0)
+ return r;
+ } else {
+
+ /* In the --user instance there's no sysinit.target,
+ * in that case require basic.target instead. */
+
+ r = unit_add_dependency_by_name(UNIT(s), UNIT_REQUIRES, SPECIAL_BASIC_TARGET, NULL, true);
+ if (r < 0)
+ return r;
+ }
+
+ /* Second, if the rest of the base system is in the same
+ * transaction, order us after it, but do not pull it in or
+ * even require it. */
+ r = unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_BASIC_TARGET, NULL, true);
if (r < 0)
return r;
- /* Second, activate normal shutdown */
+ /* Third, add us in for normal shutdown. */
return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
}
@@ -526,6 +570,43 @@ static void service_fix_output(Service *s) {
s->exec_context.std_output = UNIT(s)->manager->default_std_output;
}
+static int service_setup_bus_name(Service *s) {
+ int r;
+
+ assert(s);
+
+ if (!s->bus_name)
+ return 0;
+
+ if (is_kdbus_available()) {
+ const char *n;
+
+ n = strjoina(s->bus_name, ".busname");
+ r = unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, n, NULL, true);
+ if (r < 0)
+ return log_unit_error_errno(UNIT(s), r, "Failed to add dependency to .busname unit: %m");
+
+ } else {
+ /* If kdbus is not available, we know the dbus socket is required, hence pull it in, and require it */
+ r = unit_add_dependency_by_name(UNIT(s), UNIT_REQUIRES, SPECIAL_DBUS_SOCKET, NULL, true);
+ if (r < 0)
+ return log_unit_error_errno(UNIT(s), r, "Failed to add dependency on " SPECIAL_DBUS_SOCKET ": %m");
+ }
+
+ /* Regardless if kdbus is used or not, we always want to be ordered against dbus.socket if both are in the transaction. */
+ r = unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_DBUS_SOCKET, NULL, true);
+ if (r < 0)
+ return log_unit_error_errno(UNIT(s), r, "Failed to add dependency on " SPECIAL_DBUS_SOCKET ": %m");
+
+ r = unit_watch_bus_name(UNIT(s), s->bus_name);
+ if (r == -EEXIST)
+ return log_unit_error_errno(UNIT(s), r, "Two services allocated for the same bus name %s, refusing operation.", s->bus_name);
+ if (r < 0)
+ return log_unit_error_errno(UNIT(s), r, "Cannot watch bus name %s: %m", s->bus_name);
+
+ return 0;
+}
+
static int service_add_extras(Service *s) {
int r;
@@ -565,26 +646,13 @@ static int service_add_extras(Service *s) {
if (s->watchdog_usec > 0 && s->notify_access == NOTIFY_NONE)
s->notify_access = NOTIFY_MAIN;
- if (s->bus_name) {
- const char *n;
-
- n = strjoina(s->bus_name, ".busname");
- r = unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, n, NULL, true);
- if (r < 0)
- return r;
-
- r = unit_watch_bus_name(UNIT(s), s->bus_name);
- if (r == -EEXIST)
- return log_unit_error_errno(UNIT(s), r, "Two services allocated for the same bus name %s, refusing operation.", s->bus_name);
- if (r < 0)
- return log_unit_error_errno(UNIT(s), r, "Cannot watch bus name %s: %m", s->bus_name);
- }
+ r = service_add_default_dependencies(s);
+ if (r < 0)
+ return r;
- if (UNIT(s)->default_dependencies) {
- r = service_add_default_dependencies(s);
- if (r < 0)
- return r;
- }
+ r = service_setup_bus_name(s);
+ if (r < 0)
+ return r;
return 0;
}
@@ -884,7 +952,6 @@ static void service_set_state(Service *s, ServiceState 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;
}
static int service_coldplug(Unit *u) {
@@ -894,125 +961,143 @@ static int service_coldplug(Unit *u) {
assert(s);
assert(s->state == SERVICE_DEAD);
- if (s->deserialized_state != s->state) {
-
- if (IN_SET(s->deserialized_state,
- SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
- SERVICE_RELOAD,
- SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
- SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) {
-
- usec_t k;
+ if (s->deserialized_state == s->state)
+ return 0;
- k = IN_SET(s->deserialized_state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RELOAD) ? s->timeout_start_usec : s->timeout_stop_usec;
+ if (IN_SET(s->deserialized_state,
+ SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
+ SERVICE_RELOAD,
+ SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
+ SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) {
- /* For the start/stop timeouts 0 means off */
- if (k > 0) {
- r = service_arm_timer(s, k);
- if (r < 0)
- return r;
- }
- }
+ usec_t k;
- if (s->deserialized_state == SERVICE_AUTO_RESTART) {
+ k = IN_SET(s->deserialized_state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RELOAD) ? s->timeout_start_usec : s->timeout_stop_usec;
- /* The restart timeouts 0 means immediately */
- r = service_arm_timer(s, s->restart_usec);
- if (r < 0)
- return r;
- }
-
- if (pid_is_unwaited(s->main_pid) &&
- ((s->deserialized_state == SERVICE_START && IN_SET(s->type, SERVICE_FORKING, SERVICE_DBUS, SERVICE_ONESHOT, SERVICE_NOTIFY)) ||
- IN_SET(s->deserialized_state,
- SERVICE_START, SERVICE_START_POST,
- SERVICE_RUNNING, SERVICE_RELOAD,
- 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);
+ /* For the start/stop timeouts 0 means off */
+ if (k > 0) {
+ r = service_arm_timer(s, k);
if (r < 0)
return r;
}
+ }
- if (pid_is_unwaited(s->control_pid) &&
- IN_SET(s->deserialized_state,
- SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
- SERVICE_RELOAD,
- 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)
- return r;
- }
+ if (s->deserialized_state == SERVICE_AUTO_RESTART) {
- if (!IN_SET(s->deserialized_state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART))
- unit_watch_all_pids(UNIT(s));
+ /* The restart timeouts 0 means immediately */
+ r = service_arm_timer(s, s->restart_usec);
+ if (r < 0)
+ return r;
+ }
- if (IN_SET(s->deserialized_state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD))
- service_start_watchdog(s);
+ if (s->main_pid > 0 &&
+ pid_is_unwaited(s->main_pid) &&
+ ((s->deserialized_state == SERVICE_START && IN_SET(s->type, SERVICE_FORKING, SERVICE_DBUS, SERVICE_ONESHOT, SERVICE_NOTIFY)) ||
+ IN_SET(s->deserialized_state,
+ SERVICE_START, SERVICE_START_POST,
+ SERVICE_RUNNING, SERVICE_RELOAD,
+ 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)
+ return r;
+ }
- service_set_state(s, s->deserialized_state);
+ if (s->control_pid > 0 &&
+ pid_is_unwaited(s->control_pid) &&
+ IN_SET(s->deserialized_state,
+ SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
+ SERVICE_RELOAD,
+ 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)
+ return r;
}
+ if (!IN_SET(s->deserialized_state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART))
+ unit_watch_all_pids(UNIT(s));
+
+ if (IN_SET(s->deserialized_state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD))
+ service_start_watchdog(s);
+
+ service_set_state(s, s->deserialized_state);
return 0;
}
-static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) {
+static int service_collect_fds(Service *s, int **fds, char ***fd_names) {
+ _cleanup_strv_free_ char **rfd_names = NULL;
_cleanup_free_ int *rfds = NULL;
- unsigned rn_fds = 0;
- Iterator i;
- int r;
- Unit *u;
+ int rn_fds = 0, r;
assert(s);
assert(fds);
- assert(n_fds);
+ assert(fd_names);
- if (s->socket_fd >= 0)
- return 0;
+ if (s->socket_fd >= 0) {
- SET_FOREACH(u, UNIT(s)->dependencies[UNIT_TRIGGERED_BY], i) {
- int *cfds;
- unsigned cn_fds;
- Socket *sock;
+ /* Pass the per-connection socket */
- if (u->type != UNIT_SOCKET)
- continue;
+ rfds = new(int, 1);
+ if (!rfds)
+ return -ENOMEM;
+ rfds[0] = s->socket_fd;
- sock = SOCKET(u);
+ rfd_names = strv_new("connection", NULL);
+ if (!rfd_names)
+ return -ENOMEM;
- r = socket_collect_fds(sock, &cfds, &cn_fds);
- if (r < 0)
- return r;
+ rn_fds = 1;
+ } else {
+ Iterator i;
+ Unit *u;
- if (cn_fds <= 0) {
- free(cfds);
- continue;
- }
+ /* Pass all our configured sockets for singleton services */
- if (!rfds) {
- rfds = cfds;
- rn_fds = cn_fds;
- } else {
- int *t;
+ SET_FOREACH(u, UNIT(s)->dependencies[UNIT_TRIGGERED_BY], i) {
+ _cleanup_free_ int *cfds = NULL;
+ Socket *sock;
+ int cn_fds;
- t = realloc(rfds, (rn_fds + cn_fds) * sizeof(int));
- if (!t) {
- free(cfds);
- return -ENOMEM;
- }
+ if (u->type != UNIT_SOCKET)
+ continue;
- memcpy(t + rn_fds, cfds, cn_fds * sizeof(int));
- rfds = t;
- rn_fds += cn_fds;
+ sock = SOCKET(u);
- free(cfds);
+ cn_fds = socket_collect_fds(sock, &cfds);
+ if (cn_fds < 0)
+ return cn_fds;
+ if (cn_fds <= 0)
+ continue;
+
+ if (!rfds) {
+ rfds = cfds;
+ rn_fds = cn_fds;
+
+ cfds = NULL;
+ } else {
+ int *t;
+
+ t = realloc(rfds, (rn_fds + cn_fds) * sizeof(int));
+ if (!t)
+ return -ENOMEM;
+
+ memcpy(t + rn_fds, cfds, cn_fds * sizeof(int));
+
+ rfds = t;
+ rn_fds += cn_fds;
+ }
+
+ r = strv_extend_n(&rfd_names, socket_fdname(sock), cn_fds);
+ if (r < 0)
+ return r;
}
}
if (s->n_fd_store > 0) {
ServiceFDStore *fs;
+ char **nl;
int *t;
t = realloc(rfds, (rn_fds + s->n_fd_store) * sizeof(int));
@@ -1020,15 +1105,32 @@ static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) {
return -ENOMEM;
rfds = t;
- LIST_FOREACH(fd_store, fs, s->fd_store)
- rfds[rn_fds++] = fs->fd;
+
+ nl = realloc(rfd_names, (rn_fds + s->n_fd_store + 1) * sizeof(char*));
+ if (!nl)
+ return -ENOMEM;
+
+ rfd_names = nl;
+
+ LIST_FOREACH(fd_store, fs, s->fd_store) {
+ rfds[rn_fds] = fs->fd;
+ rfd_names[rn_fds] = strdup(strempty(fs->fdname));
+ if (!rfd_names[rn_fds])
+ return -ENOMEM;
+
+ rn_fds++;
+ }
+
+ rfd_names[rn_fds] = NULL;
}
*fds = rfds;
- *n_fds = rn_fds;
+ *fd_names = rfd_names;
rfds = NULL;
- return 0;
+ rfd_names = NULL;
+
+ return rn_fds;
}
static int service_spawn(
@@ -1042,23 +1144,25 @@ static int service_spawn(
bool is_control,
pid_t *_pid) {
- pid_t pid;
- int r;
- int *fds = NULL;
- _cleanup_free_ int *fdsbuf = NULL;
- unsigned n_fds = 0, n_env = 0;
+ _cleanup_strv_free_ char **argv = NULL, **final_env = NULL, **our_env = NULL, **fd_names = NULL;
_cleanup_free_ char *bus_endpoint_path = NULL;
- _cleanup_strv_free_ char
- **argv = NULL, **final_env = NULL, **our_env = NULL;
+ _cleanup_free_ int *fds = NULL;
+ unsigned n_fds = 0, n_env = 0;
const char *path;
+ pid_t pid;
+
ExecParameters exec_params = {
- .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
+ .apply_permissions = apply_permissions,
+ .apply_chroot = apply_chroot,
+ .apply_tty_stdin = apply_tty_stdin,
+ .bus_endpoint_fd = -1,
+ .stdin_fd = -1,
+ .stdout_fd = -1,
+ .stderr_fd = -1,
};
+ int r;
+
assert(s);
assert(c);
assert(_pid);
@@ -1078,16 +1182,11 @@ static int service_spawn(
s->exec_context.std_output == EXEC_OUTPUT_SOCKET ||
s->exec_context.std_error == EXEC_OUTPUT_SOCKET) {
- if (s->socket_fd >= 0) {
- fds = &s->socket_fd;
- n_fds = 1;
- } else {
- r = service_collect_fds(s, &fdsbuf, &n_fds);
- if (r < 0)
- goto fail;
+ r = service_collect_fds(s, &fds, &fd_names);
+ if (r < 0)
+ goto fail;
- fds = fdsbuf;
- }
+ n_fds = r;
}
if (timeout > 0) {
@@ -1125,7 +1224,7 @@ static int service_spawn(
goto fail;
}
- if (UNIT_DEREF(s->accept_socket)) {
+ if (s->socket_fd >= 0) {
union sockaddr_union sa;
socklen_t salen = sizeof(sa);
@@ -1173,7 +1272,7 @@ static int service_spawn(
if (is_control && UNIT(s)->cgroup_path) {
path = strjoina(UNIT(s)->cgroup_path, "/control");
- cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
+ (void) cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
} else
path = UNIT(s)->cgroup_path;
@@ -1191,6 +1290,7 @@ static int service_spawn(
exec_params.argv = argv;
exec_params.fds = fds;
+ exec_params.fd_names = fd_names;
exec_params.n_fds = n_fds;
exec_params.environment = final_env;
exec_params.confirm_spawn = UNIT(s)->manager->confirm_spawn;
@@ -1200,8 +1300,12 @@ static int service_spawn(
exec_params.runtime_prefix = manager_get_runtime_prefix(UNIT(s)->manager);
exec_params.watchdog_usec = s->watchdog_usec;
exec_params.bus_endpoint_path = bus_endpoint_path;
+ exec_params.selinux_context_net = s->socket_fd_selinux_context_net;
if (s->type == SERVICE_IDLE)
exec_params.idle_pipe = UNIT(s)->manager->idle_pipe;
+ exec_params.stdin_fd = s->stdin_fd;
+ exec_params.stdout_fd = s->stdout_fd;
+ exec_params.stderr_fd = s->stderr_fd;
r = exec_spawn(UNIT(s),
c,
@@ -1727,7 +1831,7 @@ fail:
}
static void service_enter_restart(Service *s) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert(s);
@@ -1747,7 +1851,7 @@ static void service_enter_restart(Service *s) {
* restarted. We use JOB_RESTART (instead of the more obvious
* JOB_START) here so that those dependency jobs will be added
* as well. */
- r = manager_add_job(UNIT(s)->manager, JOB_RESTART, UNIT(s), JOB_FAIL, false, &error, NULL);
+ r = manager_add_job(UNIT(s)->manager, JOB_RESTART, UNIT(s), JOB_FAIL, &error, NULL);
if (r < 0)
goto fail;
@@ -1778,6 +1882,7 @@ static void service_enter_reload(Service *s) {
assert(s);
service_unwatch_control_pid(s);
+ s->reload_result = SERVICE_SUCCESS;
s->control_command = s->exec_command[SERVICE_EXEC_RELOAD];
if (s->control_command) {
@@ -2001,6 +2106,7 @@ _pure_ static bool service_can_reload(Unit *u) {
static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
Service *s = SERVICE(u);
ServiceFDStore *fs;
+ int r;
assert(u);
assert(f);
@@ -2018,13 +2124,11 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
unit_serialize_item(u, f, "main-pid-known", yes_no(s->main_pid_known));
unit_serialize_item(u, f, "bus-name-good", yes_no(s->bus_name_good));
+ unit_serialize_item(u, f, "bus-name-owner", s->bus_name_owner);
- if (s->status_text) {
- _cleanup_free_ char *c = NULL;
-
- c = cescape(s->status_text);
- unit_serialize_item(u, f, "status-text", strempty(c));
- }
+ r = unit_serialize_item_escaped(u, f, "status-text", s->status_text);
+ if (r < 0)
+ return r;
/* FIXME: There's a minor uncleanliness here: if there are
* multiple commands attached here, we will start from the
@@ -2032,34 +2136,34 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
if (s->control_command_id >= 0)
unit_serialize_item(u, f, "control-command", service_exec_command_to_string(s->control_command_id));
- if (s->socket_fd >= 0) {
- int copy;
-
- copy = fdset_put_dup(fds, s->socket_fd);
- if (copy < 0)
- return copy;
-
- unit_serialize_item_format(u, f, "socket-fd", "%i", copy);
- }
-
- if (s->bus_endpoint_fd >= 0) {
- int copy;
-
- copy = fdset_put_dup(fds, s->bus_endpoint_fd);
- if (copy < 0)
- return copy;
+ r = unit_serialize_item_fd(u, f, fds, "stdin-fd", s->stdin_fd);
+ if (r < 0)
+ return r;
+ r = unit_serialize_item_fd(u, f, fds, "stdout-fd", s->stdout_fd);
+ if (r < 0)
+ return r;
+ r = unit_serialize_item_fd(u, f, fds, "stderr-fd", s->stderr_fd);
+ if (r < 0)
+ return r;
- unit_serialize_item_format(u, f, "endpoint-fd", "%i", copy);
- }
+ r = unit_serialize_item_fd(u, f, fds, "socket-fd", s->socket_fd);
+ if (r < 0)
+ return r;
+ r = unit_serialize_item_fd(u, f, fds, "endpoint-fd", s->bus_endpoint_fd);
+ if (r < 0)
+ return r;
LIST_FOREACH(fd_store, fs, s->fd_store) {
+ _cleanup_free_ char *c = NULL;
int copy;
copy = fdset_put_dup(fds, fs->fd);
if (copy < 0)
return copy;
- unit_serialize_item_format(u, f, "fd-store-fd", "%i", copy);
+ c = cescape(fs->fdname);
+
+ unit_serialize_item_format(u, f, "fd-store-fd", "%i %s", copy, strempty(c));
}
if (s->main_exec_status.pid > 0) {
@@ -2076,8 +2180,7 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
if (dual_timestamp_is_set(&s->watchdog_timestamp))
dual_timestamp_serialize(f, "watchdog-timestamp", &s->watchdog_timestamp);
- if (s->forbid_restart)
- unit_serialize_item(u, f, "forbid-restart", yes_no(s->forbid_restart));
+ unit_serialize_item(u, f, "forbid-restart", yes_no(s->forbid_restart));
return 0;
}
@@ -2149,6 +2252,10 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
log_unit_debug(u, "Failed to parse bus-name-good value: %s", value);
else
s->bus_name_good = b;
+ } else if (streq(key, "bus-name-owner")) {
+ r = free_and_strdup(&s->bus_name_owner, value);
+ if (r < 0)
+ log_unit_error_errno(u, r, "Unable to deserialize current bus owner %s: %m", value);
} else if (streq(key, "status-text")) {
char *t;
@@ -2189,12 +2296,24 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
s->bus_endpoint_fd = fdset_remove(fds, fd);
}
} else if (streq(key, "fd-store-fd")) {
+ const char *fdv;
+ size_t pf;
int fd;
- if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
+ pf = strcspn(value, WHITESPACE);
+ fdv = strndupa(value, pf);
+
+ if (safe_atoi(fdv, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
log_unit_debug(u, "Failed to parse fd-store-fd value: %s", value);
else {
- r = service_add_fd_store(s, fd);
+ _cleanup_free_ char *t = NULL;
+ const char *fdn;
+
+ fdn = value + pf;
+ fdn += strspn(fdn, WHITESPACE);
+ (void) cunescape(fdn, 0, &t);
+
+ r = service_add_fd_store(s, fd, t);
if (r < 0)
log_unit_error_errno(u, r, "Failed to add fd to store: %m");
else if (r > 0)
@@ -2236,6 +2355,33 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
log_unit_debug(u, "Failed to parse forbid-restart value: %s", value);
else
s->forbid_restart = b;
+ } else if (streq(key, "stdin-fd")) {
+ int fd;
+
+ if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
+ log_unit_debug(u, "Failed to parse stdin-fd value: %s", value);
+ else {
+ asynchronous_close(s->stdin_fd);
+ s->stdin_fd = fdset_remove(fds, fd);
+ }
+ } else if (streq(key, "stdout-fd")) {
+ int fd;
+
+ if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
+ log_unit_debug(u, "Failed to parse stdout-fd value: %s", value);
+ else {
+ asynchronous_close(s->stdout_fd);
+ s->stdout_fd = fdset_remove(fds, fd);
+ }
+ } else if (streq(key, "stderr-fd")) {
+ int fd;
+
+ if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
+ log_unit_debug(u, "Failed to parse stderr-fd value: %s", value);
+ else {
+ asynchronous_close(s->stderr_fd);
+ s->stderr_fd = fdset_remove(fds, fd);
+ }
} else
log_unit_debug(u, "Unknown serialization key: %s", key);
@@ -2273,14 +2419,6 @@ static bool service_check_gc(Unit *u) {
return false;
}
-_pure_ static bool service_check_snapshot(Unit *u) {
- Service *s = SERVICE(u);
-
- assert(s);
-
- return s->socket_fd < 0;
-}
-
static int service_retry_pid_file(Service *s) {
int r;
@@ -2948,8 +3086,17 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags, FDSet *fds)
if (strv_find(tags, "WATCHDOG=1"))
service_reset_watchdog(s);
- if (strv_find(tags, "FDSTORE=1"))
- service_add_fd_store_set(s, fds);
+ if (strv_find(tags, "FDSTORE=1")) {
+ const char *name;
+
+ name = strv_find_startswith(tags, "FDNAME=");
+ if (name && !fdname_is_valid(name)) {
+ log_unit_warning(u, "Passed FDNAME= name is invalid, ignoring.");
+ name = NULL;
+ }
+
+ service_add_fd_store_set(s, fds, name);
+ }
/* Notify clients about changed status or main pid */
if (notify_dbus)
@@ -2994,6 +3141,13 @@ static void service_bus_name_owner_change(
s->bus_name_good = !!new_owner;
+ /* Track the current owner, so we can reconstruct changes after a daemon reload */
+ r = free_and_strdup(&s->bus_name_owner, new_owner);
+ if (r < 0) {
+ log_unit_error_errno(u, r, "Unable to set new bus name owner %s: %m", new_owner);
+ return;
+ }
+
if (s->type == SERVICE_DBUS) {
/* service_enter_running() will figure out what to
@@ -3010,7 +3164,7 @@ static void service_bus_name_owner_change(
s->state == SERVICE_RUNNING ||
s->state == SERVICE_RELOAD)) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
pid_t pid;
/* Try to acquire PID from bus service */
@@ -3092,27 +3246,6 @@ static int service_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) {
return unit_kill_common(u, who, signo, s->main_pid, s->control_pid, error);
}
-static const char* const service_state_table[_SERVICE_STATE_MAX] = {
- [SERVICE_DEAD] = "dead",
- [SERVICE_START_PRE] = "start-pre",
- [SERVICE_START] = "start",
- [SERVICE_START_POST] = "start-post",
- [SERVICE_RUNNING] = "running",
- [SERVICE_EXITED] = "exited",
- [SERVICE_RELOAD] = "reload",
- [SERVICE_STOP] = "stop",
- [SERVICE_STOP_SIGABRT] = "stop-sigabrt",
- [SERVICE_STOP_SIGTERM] = "stop-sigterm",
- [SERVICE_STOP_SIGKILL] = "stop-sigkill",
- [SERVICE_STOP_POST] = "stop-post",
- [SERVICE_FINAL_SIGTERM] = "final-sigterm",
- [SERVICE_FINAL_SIGKILL] = "final-sigkill",
- [SERVICE_FAILED] = "failed",
- [SERVICE_AUTO_RESTART] = "auto-restart",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(service_state, ServiceState);
-
static const char* const service_restart_table[_SERVICE_RESTART_MAX] = {
[SERVICE_RESTART_NO] = "no",
[SERVICE_RESTART_ON_SUCCESS] = "on-success",
@@ -3214,7 +3347,6 @@ const UnitVTable service_vtable = {
.sub_state_to_string = service_sub_state_to_string,
.check_gc = service_check_gc,
- .check_snapshot = service_check_snapshot,
.sigchld_event = service_sigchld_event,
diff --git a/src/core/service.h b/src/core/service.h
index 789dff23a9..19efbccfc7 100644
--- a/src/core/service.h
+++ b/src/core/service.h
@@ -24,31 +24,10 @@
typedef struct Service Service;
typedef struct ServiceFDStore ServiceFDStore;
+#include "exit-status.h"
+#include "kill.h"
#include "path.h"
#include "ratelimit.h"
-#include "kill.h"
-#include "exit-status.h"
-
-typedef enum ServiceState {
- SERVICE_DEAD,
- SERVICE_START_PRE,
- SERVICE_START,
- SERVICE_START_POST,
- SERVICE_RUNNING,
- SERVICE_EXITED, /* Nothing is running anymore, but RemainAfterExit is true hence this is OK */
- SERVICE_RELOAD,
- SERVICE_STOP, /* No STOP_PRE state, instead just register multiple STOP executables */
- SERVICE_STOP_SIGABRT, /* Watchdog timeout */
- SERVICE_STOP_SIGTERM,
- SERVICE_STOP_SIGKILL,
- SERVICE_STOP_POST,
- SERVICE_FINAL_SIGTERM, /* In case the STOP_POST executable hangs, we shoot that down, too */
- SERVICE_FINAL_SIGKILL,
- SERVICE_FAILED,
- SERVICE_AUTO_RESTART,
- _SERVICE_STATE_MAX,
- _SERVICE_STATE_INVALID = -1
-} ServiceState;
typedef enum ServiceRestart {
SERVICE_RESTART_NO,
@@ -118,6 +97,7 @@ struct ServiceFDStore {
Service *service;
int fd;
+ char *fdname;
sd_event_source *event_source;
LIST_FIELDS(ServiceFDStore, fd_store);
@@ -192,6 +172,7 @@ struct Service {
bool reset_cpu_usage:1;
char *bus_name;
+ char *bus_name_owner; /* unique name of the current owner */
char *status_text;
int status_errno;
@@ -215,15 +196,16 @@ struct Service {
char *usb_function_descriptors;
char *usb_function_strings;
+
+ int stdin_fd;
+ int stdout_fd;
+ int stderr_fd;
};
extern const UnitVTable service_vtable;
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_;
-ServiceState service_state_from_string(const char *s) _pure_;
-
const char* service_restart_to_string(ServiceRestart i) _const_;
ServiceRestart service_restart_from_string(const char *s) _pure_;
diff --git a/src/core/show-status.c b/src/core/show-status.c
index 02b1be73e3..e4e12a3365 100644
--- a/src/core/show-status.c
+++ b/src/core/show-status.c
@@ -19,7 +19,13 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "io-util.h"
+#include "parse-util.h"
#include "show-status.h"
+#include "string-util.h"
+#include "terminal-util.h"
#include "util.h"
int parse_show_status(const char *v, ShowStatus *ret) {
@@ -40,3 +46,81 @@ int parse_show_status(const char *v, ShowStatus *ret) {
*ret = r ? SHOW_STATUS_YES : SHOW_STATUS_NO;
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;
+}
diff --git a/src/core/show-status.h b/src/core/show-status.h
index a2b2153746..c79d4acb66 100644
--- a/src/core/show-status.h
+++ b/src/core/show-status.h
@@ -21,6 +21,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <stdbool.h>
+
+#include "macro.h"
+
/* Manager status */
typedef enum ShowStatus {
@@ -32,3 +36,6 @@ typedef enum ShowStatus {
} ShowStatus;
int parse_show_status(const char *v, ShowStatus *ret);
+
+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);
diff --git a/src/core/shutdown.c b/src/core/shutdown.c
index 5296efce1d..3a95b5fd72 100644
--- a/src/core/shutdown.c
+++ b/src/core/shutdown.c
@@ -19,31 +19,34 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/mman.h>
-#include <sys/reboot.h>
-#include <linux/reboot.h>
-#include <sys/stat.h>
-#include <sys/mount.h>
#include <errno.h>
-#include <unistd.h>
+#include <getopt.h>
+#include <linux/reboot.h>
#include <signal.h>
#include <stdbool.h>
#include <stdlib.h>
-#include <getopt.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <unistd.h>
-#include "missing.h"
-#include "log.h"
+#include "alloc-util.h"
+#include "cgroup-util.h"
+#include "def.h"
#include "fileio.h"
+#include "killall.h"
+#include "log.h"
+#include "missing.h"
+#include "parse-util.h"
+#include "process-util.h"
+#include "string-util.h"
+#include "switch-root.h"
+#include "terminal-util.h"
#include "umount.h"
#include "util.h"
#include "virt.h"
#include "watchdog.h"
-#include "killall.h"
-#include "cgroup-util.h"
-#include "def.h"
-#include "switch-root.h"
-#include "process-util.h"
-#include "terminal-util.h"
#define FINALIZE_ATTEMPTS 50
@@ -430,6 +433,5 @@ int main(int argc, char *argv[]) {
error:
log_emergency_errno(r, "Critical error while doing system shutdown: %m");
-
freeze();
}
diff --git a/src/core/slice.c b/src/core/slice.c
index b414462066..06ac6f8450 100644
--- a/src/core/slice.c
+++ b/src/core/slice.c
@@ -21,13 +21,15 @@
#include <errno.h>
+#include "alloc-util.h"
+#include "dbus-slice.h"
#include "log.h"
-#include "strv.h"
+#include "slice.h"
#include "special.h"
+#include "string-util.h"
+#include "strv.h"
#include "unit-name.h"
#include "unit.h"
-#include "slice.h"
-#include "dbus-slice.h"
static const UnitActiveState state_translation_table[_SLICE_STATE_MAX] = {
[SLICE_DEAD] = UNIT_INACTIVE,
@@ -83,6 +85,9 @@ static int slice_add_default_dependencies(Slice *s) {
assert(s);
+ if (!UNIT(s)->default_dependencies)
+ return 0;
+
/* Make sure slices are unloaded on shutdown */
r = unit_add_two_dependencies_by_name(
UNIT(s),
@@ -94,7 +99,6 @@ static int slice_add_default_dependencies(Slice *s) {
return 0;
}
-
static int slice_verify(Slice *s) {
_cleanup_free_ char *parent = NULL;
int r;
@@ -142,11 +146,9 @@ static int slice_load(Unit *u) {
if (r < 0)
return r;
- if (u->default_dependencies) {
- r = slice_add_default_dependencies(s);
- if (r < 0)
- return r;
- }
+ r = slice_add_default_dependencies(s);
+ if (r < 0)
+ return r;
}
return slice_verify(s);
@@ -253,7 +255,7 @@ _pure_ static const char *slice_sub_state_to_string(Unit *u) {
return slice_state_to_string(SLICE(u)->state);
}
-static int slice_enumerate(Manager *m) {
+static void slice_enumerate(Manager *m) {
Unit *u;
int r;
@@ -262,18 +264,24 @@ static int slice_enumerate(Manager *m) {
u = manager_get_unit(m, SPECIAL_ROOT_SLICE);
if (!u) {
u = unit_new(m, sizeof(Slice));
- if (!u)
- return log_oom();
+ if (!u) {
+ log_oom();
+ return;
+ }
r = unit_add_name(u, SPECIAL_ROOT_SLICE);
if (r < 0) {
unit_free(u);
- return log_error_errno(r, "Failed to add -.slice name");
+ log_error_errno(r, "Failed to add -.slice name");
+ return;
}
}
u->default_dependencies = false;
u->no_gc = true;
+ u->ignore_on_isolate = true;
+ u->refuse_manual_start = true;
+ u->refuse_manual_stop = true;
SLICE(u)->deserialized_state = SLICE_ACTIVE;
if (!u->description)
@@ -283,17 +291,8 @@ static int slice_enumerate(Manager *m) {
unit_add_to_load_queue(u);
unit_add_to_dbus_queue(u);
-
- return 0;
}
-static const char* const slice_state_table[_SLICE_STATE_MAX] = {
- [SLICE_DEAD] = "dead",
- [SLICE_ACTIVE] = "active"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(slice_state, SliceState);
-
const UnitVTable slice_vtable = {
.object_size = sizeof(Slice),
.cgroup_context_offset = offsetof(Slice, cgroup_context),
@@ -306,6 +305,7 @@ const UnitVTable slice_vtable = {
.no_alias = true,
.no_instances = true,
+ .can_transient = true,
.load = slice_load,
diff --git a/src/core/slice.h b/src/core/slice.h
index ac648e56f8..0c356651e3 100644
--- a/src/core/slice.h
+++ b/src/core/slice.h
@@ -23,14 +23,6 @@
typedef struct Slice Slice;
-
-typedef enum SliceState {
- SLICE_DEAD,
- SLICE_ACTIVE,
- _SLICE_STATE_MAX,
- _SLICE_STATE_INVALID = -1
-} SliceState;
-
struct Slice {
Unit meta;
@@ -40,6 +32,3 @@ struct Slice {
};
extern const UnitVTable slice_vtable;
-
-const char* slice_state_to_string(SliceState i) _const_;
-SliceState slice_state_from_string(const char *s) _pure_;
diff --git a/src/core/smack-setup.c b/src/core/smack-setup.c
index cbe7d0b4a9..c9374ca0e8 100644
--- a/src/core/smack-setup.c
+++ b/src/core/smack-setup.c
@@ -21,18 +21,22 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
+#include <dirent.h>
#include <errno.h>
-#include <string.h>
-#include <stdlib.h>
#include <fcntl.h>
-#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "alloc-util.h"
+#include "dirent-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "log.h"
#include "macro.h"
#include "smack-setup.h"
+#include "string-util.h"
#include "util.h"
-#include "fileio.h"
-#include "log.h"
#ifdef HAVE_SMACK
@@ -193,6 +197,75 @@ static int write_cipso2_rules(const char* srcdir) {
return r;
}
+static int write_netlabel_rules(const char* srcdir) {
+ _cleanup_fclose_ FILE *dst = NULL;
+ _cleanup_closedir_ DIR *dir = NULL;
+ struct dirent *entry;
+ char buf[NAME_MAX];
+ int dfd = -1;
+ int r = 0;
+
+ dst = fopen("/sys/fs/smackfs/netlabel", "we");
+ if (!dst) {
+ if (errno != ENOENT)
+ log_warning_errno(errno, "Failed to open /sys/fs/smackfs/netlabel: %m");
+ return -errno; /* negative error */
+ }
+
+ /* write rules to dst from every file in the directory */
+ dir = opendir(srcdir);
+ if (!dir) {
+ if (errno != ENOENT)
+ log_warning_errno(errno, "Failed to opendir %s: %m", srcdir);
+ return errno; /* positive on purpose */
+ }
+
+ dfd = dirfd(dir);
+ assert(dfd >= 0);
+
+ FOREACH_DIRENT(entry, dir, return 0) {
+ int fd;
+ _cleanup_fclose_ FILE *policy = NULL;
+
+ fd = openat(dfd, entry->d_name, O_RDONLY|O_CLOEXEC);
+ if (fd < 0) {
+ if (r == 0)
+ r = -errno;
+ log_warning_errno(errno, "Failed to open %s: %m", entry->d_name);
+ continue;
+ }
+
+ policy = fdopen(fd, "re");
+ if (!policy) {
+ if (r == 0)
+ r = -errno;
+ safe_close(fd);
+ log_error_errno(errno, "Failed to open %s: %m", entry->d_name);
+ continue;
+ }
+
+ /* load2 write rules in the kernel require a line buffered stream */
+ FOREACH_LINE(buf, policy,
+ log_error_errno(errno, "Failed to read line from %s: %m",
+ entry->d_name)) {
+ if (!fputs(buf, dst)) {
+ if (r == 0)
+ r = -EINVAL;
+ log_error_errno(errno, "Failed to write line to /sys/fs/smackfs/netlabel");
+ break;
+ }
+ if (fflush(dst)) {
+ if (r == 0)
+ r = -errno;
+ log_error_errno(errno, "Failed to flush writes to /sys/fs/smackfs/netlabel: %m");
+ break;
+ }
+ }
+ }
+
+ return r;
+}
+
#endif
int mac_smack_setup(bool *loaded_policy) {
@@ -215,16 +288,24 @@ int mac_smack_setup(bool *loaded_policy) {
log_info("Successfully loaded Smack policies.");
break;
default:
- log_warning("Failed to load Smack access rules: %s, ignoring.",
- strerror(abs(r)));
+ log_warning_errno(r, "Failed to load Smack access rules, ignoring: %m");
return 0;
}
#ifdef SMACK_RUN_LABEL
r = write_string_file("/proc/self/attr/current", SMACK_RUN_LABEL, 0);
- if (r)
- log_warning("Failed to set SMACK label \"%s\" on self: %s",
- SMACK_RUN_LABEL, strerror(-r));
+ if (r < 0)
+ log_warning_errno(r, "Failed to set SMACK label \"" SMACK_RUN_LABEL "\" on self: %m");
+ r = write_string_file("/sys/fs/smackfs/ambient", SMACK_RUN_LABEL, 0);
+ if (r < 0)
+ log_warning_errno(r, "Failed to set SMACK ambient label \"" SMACK_RUN_LABEL "\": %m");
+ r = write_string_file("/sys/fs/smackfs/netlabel",
+ "0.0.0.0/0 " SMACK_RUN_LABEL, 0);
+ if (r < 0)
+ log_warning_errno(r, "Failed to set SMACK netlabel rule \"0.0.0.0/0 " SMACK_RUN_LABEL "\": %m");
+ r = write_string_file("/sys/fs/smackfs/netlabel", "127.0.0.1 -CIPSO", 0);
+ if (r < 0)
+ log_warning_errno(r, "Failed to set SMACK netlabel rule \"127.0.0.1 -CIPSO\": %m");
#endif
r = write_cipso2_rules("/etc/smack/cipso.d/");
@@ -234,14 +315,29 @@ int mac_smack_setup(bool *loaded_policy) {
return 0;
case ENOENT:
log_debug("Smack/CIPSO access rules directory '/etc/smack/cipso.d/' not found");
- return 0;
+ break;
case 0:
log_info("Successfully loaded Smack/CIPSO policies.");
break;
default:
- log_warning("Failed to load Smack/CIPSO access rules: %s, ignoring.",
- strerror(abs(r)));
+ log_warning_errno(r, "Failed to load Smack/CIPSO access rules, ignoring: %m");
+ break;
+ }
+
+ r = write_netlabel_rules("/etc/smack/netlabel.d/");
+ switch(r) {
+ case -ENOENT:
+ log_debug("Smack/CIPSO is not enabled in the kernel.");
return 0;
+ case ENOENT:
+ log_debug("Smack network host rules directory '/etc/smack/netlabel.d/' not found");
+ break;
+ case 0:
+ log_info("Successfully loaded Smack network host rules.");
+ break;
+ default:
+ log_warning_errno(r, "Failed to load Smack network host rules: %m, ignoring.");
+ break;
}
*loaded_policy = true;
diff --git a/src/core/snapshot.c b/src/core/snapshot.c
deleted file mode 100644
index 336ff20f84..0000000000
--- a/src/core/snapshot.c
+++ /dev/null
@@ -1,306 +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 <errno.h>
-
-#include "unit.h"
-#include "snapshot.h"
-#include "unit-name.h"
-#include "dbus-snapshot.h"
-#include "bus-common-errors.h"
-
-static const UnitActiveState state_translation_table[_SNAPSHOT_STATE_MAX] = {
- [SNAPSHOT_DEAD] = UNIT_INACTIVE,
- [SNAPSHOT_ACTIVE] = UNIT_ACTIVE
-};
-
-static void snapshot_init(Unit *u) {
- Snapshot *s = SNAPSHOT(u);
-
- assert(s);
- assert(UNIT(s)->load_state == UNIT_STUB);
-
- UNIT(s)->ignore_on_isolate = true;
- UNIT(s)->ignore_on_snapshot = true;
- UNIT(s)->allow_isolate = true;
-}
-
-static void snapshot_set_state(Snapshot *s, SnapshotState state) {
- SnapshotState old_state;
- assert(s);
-
- old_state = s->state;
- s->state = state;
-
- if (state != old_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);
-}
-
-static int snapshot_load(Unit *u) {
- Snapshot *s = SNAPSHOT(u);
-
- assert(u);
- assert(u->load_state == UNIT_STUB);
-
- /* Make sure that only snapshots created via snapshot_create()
- * can be loaded */
- if (!u->transient && UNIT(s)->manager->n_reloading <= 0)
- return -ENOENT;
-
- u->load_state = UNIT_LOADED;
- return 0;
-}
-
-static int snapshot_coldplug(Unit *u) {
- Snapshot *s = SNAPSHOT(u);
-
- assert(s);
- assert(s->state == SNAPSHOT_DEAD);
-
- if (s->deserialized_state != s->state)
- snapshot_set_state(s, s->deserialized_state);
-
- return 0;
-}
-
-static void snapshot_dump(Unit *u, FILE *f, const char *prefix) {
- Snapshot *s = SNAPSHOT(u);
-
- assert(s);
- assert(f);
-
- fprintf(f,
- "%sSnapshot State: %s\n"
- "%sClean Up: %s\n",
- prefix, snapshot_state_to_string(s->state),
- prefix, yes_no(s->cleanup));
-}
-
-static int snapshot_start(Unit *u) {
- Snapshot *s = SNAPSHOT(u);
-
- assert(s);
- assert(s->state == SNAPSHOT_DEAD);
-
- snapshot_set_state(s, SNAPSHOT_ACTIVE);
-
- if (s->cleanup)
- unit_add_to_cleanup_queue(u);
-
- return 1;
-}
-
-static int snapshot_stop(Unit *u) {
- Snapshot *s = SNAPSHOT(u);
-
- assert(s);
- assert(s->state == SNAPSHOT_ACTIVE);
-
- snapshot_set_state(s, SNAPSHOT_DEAD);
- return 1;
-}
-
-static int snapshot_serialize(Unit *u, FILE *f, FDSet *fds) {
- Snapshot *s = SNAPSHOT(u);
- Unit *other;
- Iterator i;
-
- assert(s);
- assert(f);
- assert(fds);
-
- unit_serialize_item(u, f, "state", snapshot_state_to_string(s->state));
- unit_serialize_item(u, f, "cleanup", yes_no(s->cleanup));
- SET_FOREACH(other, u->dependencies[UNIT_WANTS], i)
- unit_serialize_item(u, f, "wants", other->id);
-
- return 0;
-}
-
-static int snapshot_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
- Snapshot *s = SNAPSHOT(u);
- int r;
-
- assert(u);
- assert(key);
- assert(value);
- assert(fds);
-
- if (streq(key, "state")) {
- SnapshotState state;
-
- state = snapshot_state_from_string(value);
- if (state < 0)
- log_unit_debug(u, "Failed to parse state value: %s", value);
- else
- s->deserialized_state = state;
-
- } else if (streq(key, "cleanup")) {
-
- r = parse_boolean(value);
- if (r < 0)
- log_unit_debug(u, "Failed to parse cleanup value: %s", value);
- else
- s->cleanup = r;
-
- } else if (streq(key, "wants")) {
-
- r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_WANTS, value, NULL, true);
- if (r < 0)
- return r;
- } else
- log_unit_debug(u, "Unknown serialization key: %s", key);
-
- return 0;
-}
-
-_pure_ static UnitActiveState snapshot_active_state(Unit *u) {
- assert(u);
-
- return state_translation_table[SNAPSHOT(u)->state];
-}
-
-_pure_ static const char *snapshot_sub_state_to_string(Unit *u) {
- assert(u);
-
- return snapshot_state_to_string(SNAPSHOT(u)->state);
-}
-
-int snapshot_create(Manager *m, const char *name, bool cleanup, sd_bus_error *e, Snapshot **_s) {
- _cleanup_free_ char *n = NULL;
- Unit *other, *u = NULL;
- Iterator i;
- int r;
- const char *k;
-
- assert(m);
- assert(_s);
-
- if (name) {
- 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 (!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))
- return sd_bus_error_setf(e, BUS_ERROR_UNIT_EXISTS, "Snapshot %s exists already.", name);
-
- } else {
-
- for (;;) {
- if (asprintf(&n, "snapshot-%u.snapshot", ++ m->n_snapshots) < 0)
- return -ENOMEM;
-
- if (!manager_get_unit(m, n)) {
- name = n;
- break;
- }
-
- n = mfree(n);
- }
- }
-
- r = manager_load_unit_prepare(m, name, NULL, e, &u);
- if (r < 0)
- goto fail;
-
- u->transient = true;
- manager_dispatch_load_queue(m);
- assert(u->load_state == UNIT_LOADED);
-
- HASHMAP_FOREACH_KEY(other, k, m->units, i) {
-
- if (other->ignore_on_snapshot ||
- other->transient)
- continue;
-
- if (k != other->id)
- continue;
-
- if (UNIT_VTABLE(other)->check_snapshot)
- if (!UNIT_VTABLE(other)->check_snapshot(other))
- continue;
-
- if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
- continue;
-
- r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_WANTS, other, true);
- if (r < 0)
- goto fail;
- }
-
- SNAPSHOT(u)->cleanup = cleanup;
- *_s = SNAPSHOT(u);
-
- log_unit_info(u, "Created snapshot.");
-
- return 0;
-
-fail:
- if (u)
- unit_add_to_cleanup_queue(u);
-
- return r;
-}
-
-void snapshot_remove(Snapshot *s) {
- assert(s);
-
- log_unit_info(UNIT(s), "Removing snapshot.");
-
- unit_add_to_cleanup_queue(UNIT(s));
-}
-
-static const char* const snapshot_state_table[_SNAPSHOT_STATE_MAX] = {
- [SNAPSHOT_DEAD] = "dead",
- [SNAPSHOT_ACTIVE] = "active"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(snapshot_state, SnapshotState);
-
-const UnitVTable snapshot_vtable = {
- .object_size = sizeof(Snapshot),
-
- .no_alias = true,
- .no_instances = true,
- .no_gc = true,
-
- .init = snapshot_init,
- .load = snapshot_load,
-
- .coldplug = snapshot_coldplug,
-
- .dump = snapshot_dump,
-
- .start = snapshot_start,
- .stop = snapshot_stop,
-
- .serialize = snapshot_serialize,
- .deserialize_item = snapshot_deserialize_item,
-
- .active_state = snapshot_active_state,
- .sub_state_to_string = snapshot_sub_state_to_string,
-
- .bus_vtable = bus_snapshot_vtable
-};
diff --git a/src/core/socket.c b/src/core/socket.c
index 54e94c4f74..2e4173aabc 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -19,38 +19,46 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/stat.h>
-#include <unistd.h>
+#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
-#include <sys/epoll.h>
-#include <signal.h>
-#include <arpa/inet.h>
-#include <netinet/tcp.h>
#include <mqueue.h>
+#include <netinet/tcp.h>
+#include <signal.h>
+#include <sys/epoll.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <linux/sctp.h>
#include "sd-event.h"
+#include "alloc-util.h"
+#include "bus-error.h"
+#include "bus-util.h"
+#include "copy.h"
+#include "dbus-socket.h"
+#include "def.h"
+#include "exit-status.h"
+#include "fd-util.h"
+#include "formats-util.h"
+#include "label.h"
#include "log.h"
-#include "strv.h"
+#include "missing.h"
#include "mkdir.h"
+#include "parse-util.h"
#include "path-util.h"
-#include "unit-name.h"
-#include "unit-printf.h"
-#include "missing.h"
-#include "special.h"
-#include "label.h"
-#include "exit-status.h"
-#include "def.h"
-#include "smack-util.h"
-#include "bus-util.h"
-#include "bus-error.h"
+#include "process-util.h"
#include "selinux-util.h"
-#include "dbus-socket.h"
-#include "unit.h"
-#include "formats-util.h"
#include "signal-util.h"
+#include "smack-util.h"
#include "socket.h"
-#include "copy.h"
+#include "special.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "strv.h"
+#include "unit-name.h"
+#include "unit-printf.h"
+#include "unit.h"
+#include "user-util.h"
static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = {
[SOCKET_DEAD] = UNIT_INACTIVE,
@@ -106,11 +114,9 @@ static void socket_unwatch_control_pid(Socket *s) {
}
static void socket_cleanup_fd_list(SocketPort *p) {
- int k = p->n_auxiliary_fds;
-
- while (k--)
- safe_close(p->auxiliary_fds[k]);
+ assert(p);
+ close_many(p->auxiliary_fds, p->n_auxiliary_fds);
p->auxiliary_fds = mfree(p->auxiliary_fds);
p->n_auxiliary_fds = 0;
}
@@ -150,14 +156,16 @@ static void socket_done(Unit *u) {
s->tcp_congestion = mfree(s->tcp_congestion);
s->bind_to_device = mfree(s->bind_to_device);
- free(s->smack);
- free(s->smack_ip_in);
- free(s->smack_ip_out);
+ s->smack = mfree(s->smack);
+ s->smack_ip_in = mfree(s->smack_ip_in);
+ s->smack_ip_out = mfree(s->smack_ip_out);
strv_free(s->symlinks);
- free(s->user);
- free(s->group);
+ s->user = mfree(s->user);
+ s->group = mfree(s->group);
+
+ s->fdname = mfree(s->fdname);
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
}
@@ -283,13 +291,16 @@ static int socket_add_device_link(Socket *s) {
return 0;
t = strjoina("/sys/subsystem/net/devices/", s->bind_to_device);
- return unit_add_node_link(UNIT(s), t, false);
+ return unit_add_node_link(UNIT(s), t, false, UNIT_BINDS_TO);
}
static int socket_add_default_dependencies(Socket *s) {
int r;
assert(s);
+ if (!UNIT(s)->default_dependencies)
+ return 0;
+
r = unit_add_dependency_by_name(UNIT(s), UNIT_BEFORE, SPECIAL_SOCKETS_TARGET, NULL, true);
if (r < 0)
return r;
@@ -359,11 +370,9 @@ static int socket_add_extras(Socket *s) {
return r;
}
- if (u->default_dependencies) {
- r = socket_add_default_dependencies(s);
- if (r < 0)
- return r;
- }
+ r = socket_add_default_dependencies(s);
+ if (r < 0)
+ return r;
return 0;
}
@@ -506,6 +515,8 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
"%sPassSecurity: %s\n"
"%sTCPCongestion: %s\n"
"%sRemoveOnStop: %s\n"
+ "%sWritable: %s\n"
+ "%sFDName: %s\n"
"%sSELinuxContextFromNet: %s\n",
prefix, socket_state_to_string(s->state),
prefix, socket_result_to_string(s->result),
@@ -522,6 +533,8 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
prefix, yes_no(s->pass_sec),
prefix, strna(s->tcp_congestion),
prefix, yes_no(s->remove_on_stop),
+ prefix, yes_no(s->writable),
+ prefix, socket_fdname(s),
prefix, yes_no(s->selinux_context_from_net));
if (s->control_pid > 0)
@@ -642,7 +655,8 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
int r;
char *k = NULL;
- if ((r = socket_address_print(&p->address, &k)) < 0)
+ r = socket_address_print(&p->address, &k);
+ if (r < 0)
t = strerror(-r);
else
t = k;
@@ -863,8 +877,14 @@ static void socket_apply_socket_options(Socket *s, int fd) {
if (s->no_delay) {
int b = s->no_delay;
- if (setsockopt(fd, SOL_TCP, TCP_NODELAY, &b, sizeof(b)) < 0)
- log_unit_warning_errno(UNIT(s), errno, "TCP_NODELAY failed: %m");
+
+ if (s->socket_protocol == IPPROTO_SCTP) {
+ if (setsockopt(fd, SOL_SCTP, SCTP_NODELAY, &b, sizeof(b)) < 0)
+ log_unit_warning_errno(UNIT(s), errno, "SCTP_NODELAY failed: %m");
+ } else {
+ if (setsockopt(fd, SOL_TCP, TCP_NODELAY, &b, sizeof(b)) < 0)
+ log_unit_warning_errno(UNIT(s), errno, "TCP_NODELAY failed: %m");
+ }
}
if (s->broadcast) {
@@ -955,50 +975,48 @@ 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_errno(UNIT(s), errno, "F_SETPIPE_SZ: %m");
+ log_unit_warning_errno(UNIT(s), errno, "Setting pipe size failed, ignoring: %m");
if (s->smack) {
r = mac_smack_apply_fd(fd, SMACK_ATTR_ACCESS, s->smack);
if (r < 0)
- log_unit_error_errno(UNIT(s), r, "mac_smack_apply_fd: %m");
+ log_unit_error_errno(UNIT(s), r, "SMACK relabelling failed, ignoring: %m");
}
}
static int fifo_address_create(
const char *path,
mode_t directory_mode,
- mode_t socket_mode,
- int *_fd) {
+ mode_t socket_mode) {
- int fd = -1, r = 0;
- struct stat st;
+ _cleanup_close_ int fd = -1;
mode_t old_mask;
+ struct stat st;
+ int r;
assert(path);
- assert(_fd);
mkdir_parents_label(path, directory_mode);
r = mac_selinux_create_file_prepare(path, S_IFIFO);
if (r < 0)
- goto fail;
+ return r;
/* Enforce the right access mode for the fifo */
old_mask = umask(~ socket_mode);
/* Include the original umask in our mask */
- umask(~socket_mode | old_mask);
+ (void) umask(~socket_mode | old_mask);
r = mkfifo(path, socket_mode);
- umask(old_mask);
+ (void) umask(old_mask);
if (r < 0 && errno != EEXIST) {
r = -errno;
goto fail;
}
- fd = open(path,
- O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK | O_NOFOLLOW);
+ fd = open(path, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK | O_NOFOLLOW);
if (fd < 0) {
r = -errno;
goto fail;
@@ -1015,66 +1033,50 @@ static int fifo_address_create(
(st.st_mode & 0777) != (socket_mode & ~old_mask) ||
st.st_uid != getuid() ||
st.st_gid != getgid()) {
-
r = -EEXIST;
goto fail;
}
- *_fd = fd;
- return 0;
+ r = fd;
+ fd = -1;
+
+ return r;
fail:
mac_selinux_create_file_clear();
- safe_close(fd);
-
return r;
}
-static int special_address_create(
- const char *path,
- int *_fd) {
-
- int fd = -1, r = 0;
+static int special_address_create(const char *path, bool writable) {
+ _cleanup_close_ int fd = -1;
struct stat st;
+ int r;
assert(path);
- assert(_fd);
- fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW);
- if (fd < 0) {
- r = -errno;
- goto fail;
- }
+ fd = open(path, (writable ? O_RDWR : O_RDONLY)|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW);
+ if (fd < 0)
+ return -errno;
- if (fstat(fd, &st) < 0) {
- r = -errno;
- goto fail;
- }
+ if (fstat(fd, &st) < 0)
+ return -errno;
/* Check whether this is a /proc, /sys or /dev file or char device */
- if (!S_ISREG(st.st_mode) && !S_ISCHR(st.st_mode)) {
- r = -EEXIST;
- goto fail;
- }
-
- *_fd = fd;
- return 0;
+ if (!S_ISREG(st.st_mode) && !S_ISCHR(st.st_mode))
+ return -EEXIST;
-fail:
- safe_close(fd);
+ r = fd;
+ fd = -1;
return r;
}
-static int ffs_address_create(
- const char *path,
- int *_fd) {
-
+static int usbffs_address_create(const char *path) {
_cleanup_close_ int fd = -1;
struct stat st;
+ int r;
assert(path);
- assert(_fd);
fd = open(path, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW);
if (fd < 0)
@@ -1087,32 +1089,32 @@ static int ffs_address_create(
if (!S_ISREG(st.st_mode))
return -EEXIST;
- *_fd = fd;
+ r = fd;
fd = -1;
- return 0;
+ return r;
}
static int mq_address_create(
const char *path,
mode_t mq_mode,
long maxmsg,
- long msgsize,
- int *_fd) {
+ long msgsize) {
- int fd = -1, r = 0;
+ _cleanup_close_ int fd = -1;
struct stat st;
mode_t old_mask;
struct mq_attr _attr, *attr = NULL;
+ int r;
assert(path);
- assert(_fd);
if (maxmsg > 0 && msgsize > 0) {
- zero(_attr);
- _attr.mq_flags = O_NONBLOCK;
- _attr.mq_maxmsg = maxmsg;
- _attr.mq_msgsize = msgsize;
+ _attr = (struct mq_attr) {
+ .mq_flags = O_NONBLOCK,
+ .mq_maxmsg = maxmsg,
+ .mq_msgsize = msgsize,
+ };
attr = &_attr;
}
@@ -1120,33 +1122,24 @@ static int mq_address_create(
old_mask = umask(~ mq_mode);
/* Include the original umask in our mask */
- umask(~mq_mode | old_mask);
+ (void) umask(~mq_mode | old_mask);
fd = mq_open(path, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_CREAT, mq_mode, attr);
- umask(old_mask);
+ (void) umask(old_mask);
- if (fd < 0) {
- r = -errno;
- goto fail;
- }
+ if (fd < 0)
+ return -errno;
- if (fstat(fd, &st) < 0) {
- r = -errno;
- goto fail;
- }
+ if (fstat(fd, &st) < 0)
+ return -errno;
if ((st.st_mode & 0777) != (mq_mode & ~old_mask) ||
st.st_uid != getuid() ||
- st.st_gid != getgid()) {
-
- r = -EEXIST;
- goto fail;
- }
+ st.st_gid != getgid())
+ return -EEXIST;
- *_fd = fd;
- return 0;
+ r = fd;
+ fd = -1;
-fail:
- safe_close(fd);
return r;
}
@@ -1166,8 +1159,7 @@ static int socket_symlink(Socket *s) {
return 0;
}
-static int ffs_write_descs(int fd, Unit *u) {
- Service *s = SERVICE(u);
+static int usbffs_write_descs(int fd, Service *s) {
int r;
if (!s->usb_function_descriptors || !s->usb_function_strings)
@@ -1175,27 +1167,25 @@ static int ffs_write_descs(int fd, Unit *u) {
r = copy_file_fd(s->usb_function_descriptors, fd, false);
if (r < 0)
- return 0;
-
- r = copy_file_fd(s->usb_function_strings, fd, false);
+ return r;
- return r;
+ return copy_file_fd(s->usb_function_strings, fd, false);
}
-static int select_ep(const struct dirent *d) {
+static int usbffs_select_ep(const struct dirent *d) {
return d->d_name[0] != '.' && !streq(d->d_name, "ep0");
}
-static int ffs_dispatch_eps(SocketPort *p) {
+static int usbffs_dispatch_eps(SocketPort *p) {
_cleanup_free_ struct dirent **ent = NULL;
- int r, i, n, k;
_cleanup_free_ char *path = NULL;
+ int r, i, n, k;
- r = path_get_parent(p->path, &path);
- if (r < 0)
- return r;
+ path = dirname_malloc(p->path);
+ if (!path)
+ return -ENOMEM;
- r = scandir(path, &ent, select_ep, alphasort);
+ r = scandir(path, &ent, usbffs_select_ep, alphasort);
if (r < 0)
return -errno;
@@ -1216,10 +1206,12 @@ static int ffs_dispatch_eps(SocketPort *p) {
path_kill_slashes(ep);
- r = ffs_address_create(ep, &p->auxiliary_fds[k]);
+ r = usbffs_address_create(ep);
if (r < 0)
goto fail;
+ p->auxiliary_fds[k] = r;
+
++k;
free(ent[i]);
}
@@ -1227,9 +1219,7 @@ static int ffs_dispatch_eps(SocketPort *p) {
return r;
fail:
- while (k)
- safe_close(p->auxiliary_fds[--k]);
-
+ close_many(p->auxiliary_fds, k);
p->auxiliary_fds = mfree(p->auxiliary_fds);
p->n_auxiliary_fds = 0;
@@ -1237,10 +1227,10 @@ fail:
}
static int socket_open_fds(Socket *s) {
+ _cleanup_(mac_selinux_freep) char *label = NULL;
+ bool know_label = false;
SocketPort *p;
int r;
- char *label = NULL;
- bool know_label = false;
assert(s);
@@ -1249,7 +1239,9 @@ static int socket_open_fds(Socket *s) {
if (p->fd >= 0)
continue;
- if (p->type == SOCKET_SOCKET) {
+ switch (p->type) {
+
+ case SOCKET_SOCKET:
if (!know_label) {
/* Figure out label, if we don't it know
@@ -1282,6 +1274,19 @@ static int socket_open_fds(Socket *s) {
know_label = true;
}
+ /* Apply the socket protocol */
+ switch(p->address.type) {
+ case SOCK_STREAM:
+ case SOCK_SEQPACKET:
+ if (p->socket->socket_protocol == IPPROTO_SCTP)
+ p->address.protocol = p->socket->socket_protocol;
+ break;
+ case SOCK_DGRAM:
+ if (p->socket->socket_protocol == IPPROTO_UDPLITE)
+ p->address.protocol = p->socket->socket_protocol;
+ break;
+ }
+
r = socket_address_listen(
&p->address,
SOCK_CLOEXEC|SOCK_NONBLOCK,
@@ -1300,64 +1305,72 @@ static int socket_open_fds(Socket *s) {
p->fd = r;
socket_apply_socket_options(s, p->fd);
socket_symlink(s);
+ break;
- } else if (p->type == SOCKET_SPECIAL) {
+ case SOCKET_SPECIAL:
- r = special_address_create(
- p->path,
- &p->fd);
- if (r < 0)
+ p->fd = special_address_create(p->path, s->writable);
+ if (p->fd < 0) {
+ r = p->fd;
goto rollback;
+ }
+ break;
- } else if (p->type == SOCKET_FIFO) {
+ case SOCKET_FIFO:
- r = fifo_address_create(
+ p->fd = fifo_address_create(
p->path,
s->directory_mode,
- s->socket_mode,
- &p->fd);
- if (r < 0)
+ s->socket_mode);
+ if (p->fd < 0) {
+ r = p->fd;
goto rollback;
+ }
socket_apply_fifo_options(s, p->fd);
socket_symlink(s);
+ break;
- } else if (p->type == SOCKET_MQUEUE) {
+ case SOCKET_MQUEUE:
- r = mq_address_create(
+ p->fd = mq_address_create(
p->path,
s->socket_mode,
s->mq_maxmsg,
- s->mq_msgsize,
- &p->fd);
- if (r < 0)
+ s->mq_msgsize);
+ if (p->fd < 0) {
+ r = p->fd;
goto rollback;
- } else if (p->type == SOCKET_USB_FUNCTION) {
+ }
+ break;
- r = ffs_address_create(
- p->path,
- &p->fd);
- if (r < 0)
+ case SOCKET_USB_FUNCTION:
+
+ p->fd = usbffs_address_create(p->path);
+ if (p->fd < 0) {
+ r = p->fd;
goto rollback;
+ }
- r = ffs_write_descs(p->fd, s->service.unit);
+ r = usbffs_write_descs(p->fd, SERVICE(UNIT_DEREF(s->service)));
if (r < 0)
goto rollback;
- r = ffs_dispatch_eps(p);
+ r = usbffs_dispatch_eps(p);
if (r < 0)
goto rollback;
- } else
+
+ break;
+
+ default:
assert_not_reached("Unknown port type");
+ }
}
- mac_selinux_free(label);
return 0;
rollback:
socket_close_fds(s);
- mac_selinux_free(label);
-
return r;
}
@@ -1464,7 +1477,9 @@ static int socket_coldplug(Unit *u) {
if (s->deserialized_state == s->state)
return 0;
- if (IN_SET(s->deserialized_state,
+ if (s->control_pid > 0 &&
+ pid_is_unwaited(s->control_pid) &&
+ IN_SET(s->deserialized_state,
SOCKET_START_PRE,
SOCKET_START_CHOWN,
SOCKET_START_POST,
@@ -1475,9 +1490,6 @@ static int socket_coldplug(Unit *u) {
SOCKET_FINAL_SIGTERM,
SOCKET_FINAL_SIGKILL)) {
- if (s->control_pid <= 0)
- return -EBADMSG;
-
r = unit_watch_pid(UNIT(s), s->control_pid);
if (r < 0)
return r;
@@ -1519,6 +1531,9 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
.apply_chroot = true,
.apply_tty_stdin = true,
.bus_endpoint_fd = -1,
+ .stdin_fd = -1,
+ .stdout_fd = -1,
+ .stderr_fd = -1,
};
assert(s);
@@ -1873,7 +1888,7 @@ fail:
}
static void socket_enter_running(Socket *s, int cfd) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert(s);
@@ -1927,7 +1942,7 @@ static void socket_enter_running(Socket *s, int cfd) {
goto fail;
}
- r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT_DEREF(s->service), JOB_REPLACE, true, &error, NULL);
+ r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT_DEREF(s->service), JOB_REPLACE, &error, NULL);
if (r < 0)
goto fail;
}
@@ -1985,7 +2000,7 @@ static void socket_enter_running(Socket *s, int cfd) {
cfd = -1;
s->n_connections ++;
- r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, true, &error, NULL);
+ r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, &error, NULL);
if (r < 0)
goto fail;
@@ -2339,7 +2354,7 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
return 0;
}
-static int socket_distribute_fds(Unit *u, FDSet *fds) {
+static void socket_distribute_fds(Unit *u, FDSet *fds) {
Socket *s = SOCKET(u);
SocketPort *p;
@@ -2363,8 +2378,6 @@ static int socket_distribute_fds(Unit *u, FDSet *fds) {
}
}
}
-
- return 0;
}
_pure_ static UnitActiveState socket_active_state(Unit *u) {
@@ -2644,49 +2657,43 @@ static int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *use
return 0;
}
-int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds) {
- int *rfds;
- unsigned rn_fds, k;
- int i;
+int socket_collect_fds(Socket *s, int **fds) {
+ int *rfds, k = 0, n = 0;
SocketPort *p;
assert(s);
assert(fds);
- assert(n_fds);
/* Called from the service code for requesting our fds */
- rn_fds = 0;
LIST_FOREACH(port, p, s->ports) {
if (p->fd >= 0)
- rn_fds++;
- rn_fds += p->n_auxiliary_fds;
+ n++;
+ n += p->n_auxiliary_fds;
}
- if (rn_fds <= 0) {
+ if (n <= 0) {
*fds = NULL;
- *n_fds = 0;
return 0;
}
- rfds = new(int, rn_fds);
+ rfds = new(int, n);
if (!rfds)
return -ENOMEM;
- k = 0;
LIST_FOREACH(port, p, s->ports) {
+ int i;
+
if (p->fd >= 0)
rfds[k++] = p->fd;
for (i = 0; i < p->n_auxiliary_fds; ++i)
rfds[k++] = p->auxiliary_fds[i];
}
- assert(k == rn_fds);
+ assert(k == n);
*fds = rfds;
- *n_fds = rn_fds;
-
- return 0;
+ return n;
}
static void socket_reset_failed(Unit *u) {
@@ -2782,23 +2789,18 @@ static int socket_get_timeout(Unit *u, uint64_t *timeout) {
return 1;
}
-static const char* const socket_state_table[_SOCKET_STATE_MAX] = {
- [SOCKET_DEAD] = "dead",
- [SOCKET_START_PRE] = "start-pre",
- [SOCKET_START_CHOWN] = "start-chown",
- [SOCKET_START_POST] = "start-post",
- [SOCKET_LISTENING] = "listening",
- [SOCKET_RUNNING] = "running",
- [SOCKET_STOP_PRE] = "stop-pre",
- [SOCKET_STOP_PRE_SIGTERM] = "stop-pre-sigterm",
- [SOCKET_STOP_PRE_SIGKILL] = "stop-pre-sigkill",
- [SOCKET_STOP_POST] = "stop-post",
- [SOCKET_FINAL_SIGTERM] = "final-sigterm",
- [SOCKET_FINAL_SIGKILL] = "final-sigkill",
- [SOCKET_FAILED] = "failed"
-};
+char *socket_fdname(Socket *s) {
+ assert(s);
+
+ /* Returns the name to use for $LISTEN_NAMES. If the user
+ * didn't specify anything specifically, use the socket unit's
+ * name as fallback. */
-DEFINE_STRING_TABLE_LOOKUP(socket_state, SocketState);
+ if (s->fdname)
+ return s->fdname;
+
+ return UNIT(s)->id;
+}
static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = {
[SOCKET_EXEC_START_PRE] = "StartPre",
diff --git a/src/core/socket.h b/src/core/socket.h
index 286397b41c..08033287a6 100644
--- a/src/core/socket.h
+++ b/src/core/socket.h
@@ -23,27 +23,9 @@
typedef struct Socket Socket;
-#include "socket-util.h"
#include "mount.h"
#include "service.h"
-
-typedef enum SocketState {
- SOCKET_DEAD,
- SOCKET_START_PRE,
- SOCKET_START_CHOWN,
- SOCKET_START_POST,
- SOCKET_LISTENING,
- SOCKET_RUNNING,
- SOCKET_STOP_PRE,
- SOCKET_STOP_PRE_SIGTERM,
- SOCKET_STOP_PRE_SIGKILL,
- SOCKET_STOP_POST,
- SOCKET_FINAL_SIGTERM,
- SOCKET_FINAL_SIGKILL,
- SOCKET_FAILED,
- _SOCKET_STATE_MAX,
- _SOCKET_STATE_INVALID = -1
-} SocketState;
+#include "socket-util.h"
typedef enum SocketExecCommand {
SOCKET_EXEC_START_PRE,
@@ -136,6 +118,9 @@ struct Socket {
bool accept;
bool remove_on_stop;
+ bool writable;
+
+ int socket_protocol;
/* Socket options */
bool keep_alive;
@@ -171,20 +156,23 @@ struct Socket {
char *user, *group;
bool reset_cpu_usage:1;
+
+ char *fdname;
};
/* Called from the service code when collecting fds */
-int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds);
+int socket_collect_fds(Socket *s, int **fds);
/* Called from the service code when a per-connection service ended */
void socket_connection_unref(Socket *s);
void socket_free_ports(Socket *s);
-extern const UnitVTable socket_vtable;
+int socket_instantiate_service(Socket *s);
+
+char *socket_fdname(Socket *s);
-const char* socket_state_to_string(SocketState i) _const_;
-SocketState socket_state_from_string(const char *s) _pure_;
+extern const UnitVTable socket_vtable;
const char* socket_exec_command_to_string(SocketExecCommand i) _const_;
SocketExecCommand socket_exec_command_from_string(const char *s) _pure_;
@@ -193,5 +181,3 @@ const char* socket_result_to_string(SocketResult i) _const_;
SocketResult socket_result_from_string(const char *s) _pure_;
const char* socket_port_type_to_string(SocketPort *p) _pure_;
-
-int socket_instantiate_service(Socket *s);
diff --git a/src/core/swap.c b/src/core/swap.c
index bef457069f..5568898bd7 100644
--- a/src/core/swap.c
+++ b/src/core/swap.c
@@ -20,22 +20,30 @@
***/
#include <errno.h>
-#include <unistd.h>
#include <sys/epoll.h>
#include <sys/stat.h>
-#include <libudev.h>
+#include <unistd.h>
-#include "unit.h"
-#include "swap.h"
-#include "unit-name.h"
+#include "libudev.h"
+
+#include "alloc-util.h"
#include "dbus-swap.h"
-#include "special.h"
+#include "escape.h"
#include "exit-status.h"
+#include "fd-util.h"
+#include "formats-util.h"
+#include "fstab-util.h"
+#include "parse-util.h"
#include "path-util.h"
-#include "virt.h"
+#include "process-util.h"
+#include "special.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "swap.h"
#include "udev-util.h"
-#include "fstab-util.h"
-#include "formats-util.h"
+#include "unit-name.h"
+#include "unit.h"
+#include "virt.h"
static const UnitActiveState state_translation_table[_SWAP_STATE_MAX] = {
[SWAP_DEAD] = UNIT_INACTIVE,
@@ -194,7 +202,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 == MANAGER_SYSTEM);
+ return unit_add_node_link(UNIT(s), s->what, UNIT(s)->manager->running_as == MANAGER_SYSTEM, UNIT_BINDS_TO);
else
/* File based swap devices need to be ordered after
* systemd-remount-fs.service, since they might need a
@@ -203,14 +211,25 @@ static int swap_add_device_links(Swap *s) {
}
static int swap_add_default_dependencies(Swap *s) {
+ int r;
+
assert(s);
+ if (!UNIT(s)->default_dependencies)
+ return 0;
+
if (UNIT(s)->manager->running_as != MANAGER_SYSTEM)
return 0;
if (detect_container() > 0)
return 0;
+ /* swap units generated for the swap dev links are missing the
+ * ordering dep against the swap target. */
+ r = unit_add_dependency_by_name(UNIT(s), UNIT_BEFORE, SPECIAL_SWAP_TARGET, NULL, true);
+ if (r < 0)
+ return r;
+
return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true);
}
@@ -323,11 +342,9 @@ static int swap_load(Unit *u) {
if (r < 0)
return r;
- if (UNIT(s)->default_dependencies) {
- r = swap_add_default_dependencies(s);
- if (r < 0)
- return r;
- }
+ r = swap_add_default_dependencies(s);
+ if (r < 0)
+ return r;
}
return swap_verify(s);
@@ -520,16 +537,16 @@ static int swap_coldplug(Unit *u) {
if (new_state == s->state)
return 0;
- if (new_state == SWAP_ACTIVATING ||
- new_state == SWAP_ACTIVATING_SIGTERM ||
- new_state == SWAP_ACTIVATING_SIGKILL ||
- new_state == SWAP_ACTIVATING_DONE ||
- new_state == SWAP_DEACTIVATING ||
- new_state == SWAP_DEACTIVATING_SIGTERM ||
- new_state == SWAP_DEACTIVATING_SIGKILL) {
-
- if (s->control_pid <= 0)
- return -EBADMSG;
+ if (s->control_pid > 0 &&
+ pid_is_unwaited(s->control_pid) &&
+ IN_SET(new_state,
+ SWAP_ACTIVATING,
+ SWAP_ACTIVATING_SIGTERM,
+ SWAP_ACTIVATING_SIGKILL,
+ SWAP_ACTIVATING_DONE,
+ SWAP_DEACTIVATING,
+ SWAP_DEACTIVATING_SIGTERM,
+ SWAP_DEACTIVATING_SIGKILL)) {
r = unit_watch_pid(UNIT(s), s->control_pid);
if (r < 0)
@@ -597,6 +614,9 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
.apply_chroot = true,
.apply_tty_stdin = true,
.bus_endpoint_fd = -1,
+ .stdin_fd = -1,
+ .stdout_fd = -1,
+ .stderr_fd = -1,
};
assert(s);
@@ -1195,7 +1215,7 @@ static Unit *swap_following(Unit *u) {
if (other->from_fragment)
return UNIT(other);
- /* Otherwise make everybody follow the unit that's named after
+ /* Otherwise, make everybody follow the unit that's named after
* the swap device in the kernel */
if (streq_ptr(s->what, s->devnode))
@@ -1257,26 +1277,36 @@ static void swap_shutdown(Manager *m) {
m->swaps_by_devnode = hashmap_free(m->swaps_by_devnode);
}
-static int swap_enumerate(Manager *m) {
+static void swap_enumerate(Manager *m) {
int r;
assert(m);
if (!m->proc_swaps) {
m->proc_swaps = fopen("/proc/swaps", "re");
- if (!m->proc_swaps)
- return errno == ENOENT ? 0 : -errno;
+ if (!m->proc_swaps) {
+ if (errno == ENOENT)
+ log_debug("Not swap enabled, skipping enumeration");
+ else
+ log_error_errno(errno, "Failed to open /proc/swaps: %m");
+
+ return;
+ }
r = sd_event_add_io(m->event, &m->swap_event_source, fileno(m->proc_swaps), EPOLLPRI, swap_dispatch_io, m);
- if (r < 0)
+ if (r < 0) {
+ log_error_errno(r, "Failed to watch /proc/swaps: %m");
goto fail;
+ }
/* Dispatch this before we dispatch SIGCHLD, so that
* we always get the events from /proc/swaps before
* the SIGCHLD of /sbin/swapon. */
r = sd_event_source_set_priority(m->swap_event_source, -10);
- if (r < 0)
+ if (r < 0) {
+ log_error_errno(r, "Failed to change /proc/swaps priority: %m");
goto fail;
+ }
(void) sd_event_source_set_description(m->swap_event_source, "swap-proc");
}
@@ -1285,11 +1315,10 @@ static int swap_enumerate(Manager *m) {
if (r < 0)
goto fail;
- return 0;
+ return;
fail:
swap_shutdown(m);
- return r;
}
int swap_process_device_new(Manager *m, struct udev_device *dev) {
@@ -1398,21 +1427,6 @@ static bool swap_supported(void) {
return supported;
}
-static const char* const swap_state_table[_SWAP_STATE_MAX] = {
- [SWAP_DEAD] = "dead",
- [SWAP_ACTIVATING] = "activating",
- [SWAP_ACTIVATING_DONE] = "activating-done",
- [SWAP_ACTIVE] = "active",
- [SWAP_DEACTIVATING] = "deactivating",
- [SWAP_ACTIVATING_SIGTERM] = "activating-sigterm",
- [SWAP_ACTIVATING_SIGKILL] = "activating-sigkill",
- [SWAP_DEACTIVATING_SIGTERM] = "deactivating-sigterm",
- [SWAP_DEACTIVATING_SIGKILL] = "deactivating-sigkill",
- [SWAP_FAILED] = "failed"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(swap_state, SwapState);
-
static const char* const swap_exec_command_table[_SWAP_EXEC_COMMAND_MAX] = {
[SWAP_EXEC_ACTIVATE] = "ExecActivate",
[SWAP_EXEC_DEACTIVATE] = "ExecDeactivate",
diff --git a/src/core/swap.h b/src/core/swap.h
index 9136b9abab..303b926568 100644
--- a/src/core/swap.h
+++ b/src/core/swap.h
@@ -22,26 +22,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <libudev.h>
+#include "libudev.h"
typedef struct Swap Swap;
-
-typedef enum SwapState {
- SWAP_DEAD,
- SWAP_ACTIVATING, /* /sbin/swapon is running, but the swap not yet enabled. */
- SWAP_ACTIVATING_DONE, /* /sbin/swapon is running, and the swap is done. */
- SWAP_ACTIVE,
- SWAP_DEACTIVATING,
- SWAP_ACTIVATING_SIGTERM,
- SWAP_ACTIVATING_SIGKILL,
- SWAP_DEACTIVATING_SIGTERM,
- SWAP_DEACTIVATING_SIGKILL,
- SWAP_FAILED,
- _SWAP_STATE_MAX,
- _SWAP_STATE_INVALID = -1
-} SwapState;
-
typedef enum SwapExecCommand {
SWAP_EXEC_ACTIVATE,
SWAP_EXEC_DEACTIVATE,
@@ -120,9 +104,6 @@ extern const UnitVTable swap_vtable;
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_;
-
const char* swap_exec_command_to_string(SwapExecCommand i) _const_;
SwapExecCommand swap_exec_command_from_string(const char *s) _pure_;
diff --git a/src/core/system.conf b/src/core/system.conf
index 231609033b..e2ded27333 100644
--- a/src/core/system.conf
+++ b/src/core/system.conf
@@ -17,9 +17,10 @@
#LogColor=yes
#LogLocation=no
#DumpCore=yes
-#CrashShell=no
#ShowStatus=yes
-#CrashChVT=1
+#CrashChangeVT=no
+#CrashShell=no
+#CrashReboot=no
#CPUAffinity=1 2
#JoinControllers=cpu,cpuacct net_cls,net_prio
#RuntimeWatchdogSec=0
@@ -39,6 +40,8 @@
#DefaultCPUAccounting=no
#DefaultBlockIOAccounting=no
#DefaultMemoryAccounting=no
+#DefaultTasksAccounting=yes
+#DefaultTasksMax=512
#DefaultLimitCPU=
#DefaultLimitFSIZE=
#DefaultLimitDATA=
diff --git a/src/core/target.c b/src/core/target.c
index f714cb31c2..14f9b2e26a 100644
--- a/src/core/target.c
+++ b/src/core/target.c
@@ -19,13 +19,13 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-
-#include "unit.h"
-#include "target.h"
-#include "log.h"
#include "dbus-target.h"
+#include "log.h"
#include "special.h"
+#include "string-util.h"
#include "unit-name.h"
+#include "unit.h"
+#include "target.h"
static const UnitActiveState state_translation_table[_TARGET_STATE_MAX] = {
[TARGET_DEAD] = UNIT_INACTIVE,
@@ -52,9 +52,7 @@ static int target_add_default_dependencies(Target *t) {
static const UnitDependency deps[] = {
UNIT_REQUIRES,
- UNIT_REQUIRES_OVERRIDABLE,
UNIT_REQUISITE,
- UNIT_REQUISITE_OVERRIDABLE,
UNIT_WANTS,
UNIT_BINDS_TO,
UNIT_PART_OF
@@ -192,13 +190,6 @@ _pure_ static const char *target_sub_state_to_string(Unit *u) {
return target_state_to_string(TARGET(u)->state);
}
-static const char* const target_state_table[_TARGET_STATE_MAX] = {
- [TARGET_DEAD] = "dead",
- [TARGET_ACTIVE] = "active"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(target_state, TargetState);
-
const UnitVTable target_vtable = {
.object_size = sizeof(Target),
diff --git a/src/core/target.h b/src/core/target.h
index 0a25ef469a..3cc6c07bfa 100644
--- a/src/core/target.h
+++ b/src/core/target.h
@@ -23,14 +23,6 @@
typedef struct Target Target;
-
-typedef enum TargetState {
- TARGET_DEAD,
- TARGET_ACTIVE,
- _TARGET_STATE_MAX,
- _TARGET_STATE_INVALID = -1
-} TargetState;
-
struct Target {
Unit meta;
@@ -38,6 +30,3 @@ struct Target {
};
extern const UnitVTable target_vtable;
-
-const char* target_state_to_string(TargetState i) _const_;
-TargetState target_state_from_string(const char *s) _pure_;
diff --git a/src/core/timer.c b/src/core/timer.c
index eb6567bbfa..a3c8ac72e8 100644
--- a/src/core/timer.c
+++ b/src/core/timer.c
@@ -21,13 +21,21 @@
#include <errno.h>
-#include "unit.h"
-#include "unit-name.h"
-#include "timer.h"
+#include "alloc-util.h"
+#include "bus-error.h"
+#include "bus-util.h"
#include "dbus-timer.h"
+#include "fs-util.h"
+#include "parse-util.h"
+#include "random-util.h"
#include "special.h"
-#include "bus-util.h"
-#include "bus-error.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "timer.h"
+#include "unit-name.h"
+#include "unit.h"
+#include "user-util.h"
+#include "virt.h"
static const UnitActiveState state_translation_table[_TIMER_STATE_MAX] = {
[TIMER_DEAD] = UNIT_INACTIVE,
@@ -48,6 +56,7 @@ static void timer_init(Unit *u) {
t->next_elapse_monotonic_or_boottime = USEC_INFINITY;
t->next_elapse_realtime = USEC_INFINITY;
t->accuracy_usec = u->manager->default_timer_accuracy_usec;
+ t->remain_after_elapse = true;
}
void timer_free_values(Timer *t) {
@@ -95,6 +104,9 @@ static int timer_add_default_dependencies(Timer *t) {
assert(t);
+ if (!UNIT(t)->default_dependencies)
+ return 0;
+
r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_TIMERS_TARGET, NULL, true);
if (r < 0)
return r;
@@ -185,11 +197,9 @@ static int timer_load(Unit *u) {
if (r < 0)
return r;
- if (u->default_dependencies) {
- r = timer_add_default_dependencies(t);
- if (r < 0)
- return r;
- }
+ r = timer_add_default_dependencies(t);
+ if (r < 0)
+ return r;
}
return timer_verify(t);
@@ -209,13 +219,15 @@ static void timer_dump(Unit *u, FILE *f, const char *prefix) {
"%sUnit: %s\n"
"%sPersistent: %s\n"
"%sWakeSystem: %s\n"
- "%sAccuracy: %s\n",
+ "%sAccuracy: %s\n"
+ "%sRemainAfterElapse: %s\n",
prefix, timer_state_to_string(t->state),
prefix, timer_result_to_string(t->result),
prefix, trigger ? trigger->id : "n/a",
prefix, yes_no(t->persistent),
prefix, yes_no(t->wake_system),
- prefix, format_timespan(buf, sizeof(buf), t->accuracy_usec, 1));
+ prefix, format_timespan(buf, sizeof(buf), t->accuracy_usec, 1),
+ prefix, yes_no(t->remain_after_elapse));
LIST_FOREACH(value, v, t->values) {
@@ -267,13 +279,13 @@ static int timer_coldplug(Unit *u) {
assert(t);
assert(t->state == TIMER_DEAD);
- if (t->deserialized_state != t->state) {
+ if (t->deserialized_state == t->state)
+ return 0;
- if (t->deserialized_state == TIMER_WAITING)
- timer_enter_waiting(t, false);
- else
- timer_set_state(t, t->deserialized_state);
- }
+ if (t->deserialized_state == TIMER_WAITING)
+ timer_enter_waiting(t, false);
+ else
+ timer_set_state(t, t->deserialized_state);
return 0;
}
@@ -287,6 +299,23 @@ static void timer_enter_dead(Timer *t, TimerResult f) {
timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD);
}
+static void timer_enter_elapsed(Timer *t, bool leave_around) {
+ assert(t);
+
+ /* If a unit is marked with RemainAfterElapse=yes we leave it
+ * around even after it elapsed once, so that starting it
+ * later again does not necessarily mean immediate
+ * retriggering. We unconditionally leave units with
+ * TIMER_UNIT_ACTIVE or TIMER_UNIT_INACTIVE triggers around,
+ * since they might be restarted automatically at any time
+ * later on. */
+
+ if (t->remain_after_elapse || leave_around)
+ timer_set_state(t, TIMER_ELAPSED);
+ else
+ timer_enter_dead(t, TIMER_SUCCESS);
+}
+
static usec_t monotonic_to_boottime(usec_t t) {
usec_t a, b;
@@ -302,10 +331,33 @@ static usec_t monotonic_to_boottime(usec_t t) {
return 0;
}
+static void add_random(Timer *t, usec_t *v) {
+ char s[FORMAT_TIMESPAN_MAX];
+ usec_t add;
+
+ assert(t);
+ assert(*v);
+
+ if (t->random_usec == 0)
+ return;
+ if (*v == USEC_INFINITY)
+ return;
+
+ add = random_u64() % t->random_usec;
+
+ if (*v + add < *v) /* overflow */
+ *v = (usec_t) -2; /* Highest possible value, that is not USEC_INFINITY */
+ else
+ *v += add;
+
+ log_unit_info(UNIT(t), "Adding %s random time.", format_timespan(s, sizeof(s), add, 0));
+}
+
static void timer_enter_waiting(Timer *t, bool initial) {
bool found_monotonic = false, found_realtime = false;
usec_t ts_realtime, ts_monotonic;
usec_t base = 0;
+ bool leave_around = false;
TimerValue *v;
int r;
@@ -353,16 +405,20 @@ static void timer_enter_waiting(Timer *t, bool initial) {
break;
case TIMER_BOOT:
- /* CLOCK_MONOTONIC equals the uptime on Linux */
- base = 0;
- break;
-
+ if (detect_container() <= 0) {
+ /* CLOCK_MONOTONIC equals the uptime on Linux */
+ base = 0;
+ break;
+ }
+ /* In a container we don't want to include the time the host
+ * was already up when the container started, so count from
+ * our own startup. Fall through. */
case TIMER_STARTUP:
base = UNIT(t)->manager->userspace_timestamp.monotonic;
break;
case TIMER_UNIT_ACTIVE:
-
+ leave_around = true;
base = UNIT_TRIGGER(UNIT(t))->inactive_exit_timestamp.monotonic;
if (base <= 0)
@@ -374,7 +430,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
break;
case TIMER_UNIT_INACTIVE:
-
+ leave_around = true;
base = UNIT_TRIGGER(UNIT(t))->inactive_enter_timestamp.monotonic;
if (base <= 0)
@@ -411,14 +467,18 @@ static void timer_enter_waiting(Timer *t, bool initial) {
if (!found_monotonic && !found_realtime) {
log_unit_debug(UNIT(t), "Timer is elapsed.");
- timer_set_state(t, TIMER_ELAPSED);
+ timer_enter_elapsed(t, leave_around);
return;
}
if (found_monotonic) {
char buf[FORMAT_TIMESPAN_MAX];
+ usec_t left;
- 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));
+ add_random(t, &t->next_elapse_monotonic_or_boottime);
+
+ left = t->next_elapse_monotonic_or_boottime > ts_monotonic ? t->next_elapse_monotonic_or_boottime - ts_monotonic : 0;
+ log_unit_debug(UNIT(t), "Monotonic timer elapses in %s.", format_timespan(buf, sizeof(buf), left, 0));
if (t->monotonic_event_source) {
r = sd_event_source_set_time(t->monotonic_event_source, t->next_elapse_monotonic_or_boottime);
@@ -451,6 +511,9 @@ static void timer_enter_waiting(Timer *t, bool initial) {
if (found_realtime) {
char buf[FORMAT_TIMESTAMP_MAX];
+
+ add_random(t, &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) {
@@ -490,7 +553,7 @@ fail:
}
static void timer_enter_running(Timer *t) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert(t);
@@ -499,15 +562,14 @@ static void timer_enter_running(Timer *t) {
if (unit_stop_pending(UNIT(t)))
return;
- r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_TRIGGER(UNIT(t)),
- JOB_REPLACE, true, &error, NULL);
+ r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_TRIGGER(UNIT(t)), JOB_REPLACE, &error, NULL);
if (r < 0)
goto fail;
dual_timestamp_get(&t->last_trigger);
if (t->stamp_path)
- touch_file(t->stamp_path, true, t->last_trigger.realtime, UID_INVALID, GID_INVALID, 0);
+ touch_file(t->stamp_path, true, t->last_trigger.realtime, UID_INVALID, GID_INVALID, MODE_INVALID);
timer_set_state(t, TIMER_RUNNING);
return;
@@ -543,7 +605,7 @@ static int timer_start(Unit *u) {
/* The timer has never run before,
* make sure a stamp file exists.
*/
- touch_file(t->stamp_path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0);
+ touch_file(t->stamp_path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID);
}
t->result = TIMER_SUCCESS;
@@ -713,16 +775,6 @@ static void timer_time_change(Unit *u) {
timer_enter_waiting(t, false);
}
-static const char* const timer_state_table[_TIMER_STATE_MAX] = {
- [TIMER_DEAD] = "dead",
- [TIMER_WAITING] = "waiting",
- [TIMER_RUNNING] = "running",
- [TIMER_ELAPSED] = "elapsed",
- [TIMER_FAILED] = "failed"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState);
-
static const char* const timer_base_table[_TIMER_BASE_MAX] = {
[TIMER_ACTIVE] = "OnActiveSec",
[TIMER_BOOT] = "OnBootSec",
diff --git a/src/core/timer.h b/src/core/timer.h
index 9d919e4d3e..0599f07818 100644
--- a/src/core/timer.h
+++ b/src/core/timer.h
@@ -25,16 +25,6 @@ typedef struct Timer Timer;
#include "calendarspec.h"
-typedef enum TimerState {
- TIMER_DEAD,
- TIMER_WAITING,
- TIMER_RUNNING,
- TIMER_ELAPSED,
- TIMER_FAILED,
- _TIMER_STATE_MAX,
- _TIMER_STATE_INVALID = -1
-} TimerState;
-
typedef enum TimerBase {
TIMER_ACTIVE,
TIMER_BOOT,
@@ -68,6 +58,7 @@ struct Timer {
Unit meta;
usec_t accuracy_usec;
+ usec_t random_usec;
LIST_HEAD(TimerValue, values);
usec_t next_elapse_realtime;
@@ -83,6 +74,7 @@ struct Timer {
bool persistent;
bool wake_system;
+ bool remain_after_elapse;
char *stamp_path;
};
@@ -91,9 +83,6 @@ void timer_free_values(Timer *t);
extern const UnitVTable timer_vtable;
-const char *timer_state_to_string(TimerState i) _const_;
-TimerState timer_state_from_string(const char *s) _pure_;
-
const char *timer_base_to_string(TimerBase i) _const_;
TimerBase timer_base_from_string(const char *s) _pure_;
diff --git a/src/core/transaction.c b/src/core/transaction.c
index d1c1b9a3cd..8b0ed74643 100644
--- a/src/core/transaction.c
+++ b/src/core/transaction.c
@@ -19,13 +19,15 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
#include <fcntl.h>
+#include <unistd.h>
+#include "alloc-util.h"
#include "bus-common-errors.h"
#include "bus-error.h"
-#include "transaction.h"
#include "terminal-util.h"
+#include "transaction.h"
+#include "dbus-unit.h"
static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies);
@@ -98,9 +100,7 @@ static void transaction_merge_and_delete_job(Transaction *tr, Job *j, Job *other
j->type = t;
j->state = JOB_WAITING;
- j->override = j->override || other->override;
j->irreversible = j->irreversible || other->irreversible;
-
j->matters_to_anchor = j->matters_to_anchor || other->matters_to_anchor;
/* Patch us in as new owner of the JobDependency objects */
@@ -744,7 +744,7 @@ int transaction_activate(Transaction *tr, Manager *m, JobMode mode, sd_bus_error
return 0;
}
-static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, bool override, bool *is_new) {
+static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, bool *is_new) {
Job *j, *f;
assert(tr);
@@ -773,7 +773,6 @@ static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, b
j->generation = 0;
j->marker = NULL;
j->matters_to_anchor = false;
- j->override = override;
j->irreversible = tr->irreversible;
LIST_PREPEND(transaction, f, j);
@@ -832,7 +831,6 @@ int transaction_add_job_and_dependencies(
Unit *unit,
Job *by,
bool matters,
- bool override,
bool conflicts,
bool ignore_requirements,
bool ignore_order,
@@ -863,30 +861,12 @@ int transaction_add_job_and_dependencies(
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);
- if (type != JOB_STOP && unit->load_state == UNIT_ERROR) {
- if (unit->load_error == -ENOENT || unit->manager->test_run)
- return sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED,
- "Unit %s failed to load: %s.",
- unit->id,
- strerror(-unit->load_error));
- else
- return sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED,
- "Unit %s failed to load: %s. "
- "See system logs and 'systemctl status %s' for details.",
- unit->id,
- strerror(-unit->load_error),
- unit->id);
+ if (type != JOB_STOP) {
+ r = bus_unit_check_load_state(unit, e);
+ if (r < 0)
+ return r;
}
- if (type != JOB_STOP && unit->load_state == UNIT_NOT_FOUND)
- return sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED,
- "Unit %s failed to load: %s.",
- unit->id, strerror(-unit->load_error));
-
- if (type != JOB_STOP && unit->load_state == UNIT_MASKED)
- return sd_bus_error_setf(e, BUS_ERROR_UNIT_MASKED,
- "Unit %s is masked.", unit->id);
-
if (!unit_job_is_applicable(unit, type))
return sd_bus_error_setf(e, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE,
"Job type %s is not applicable for unit %s.",
@@ -894,7 +874,7 @@ int transaction_add_job_and_dependencies(
/* First add the job. */
- ret = transaction_add_one_job(tr, type, unit, override, &is_new);
+ ret = transaction_add_one_job(tr, type, unit, &is_new);
if (!ret)
return -ENOMEM;
@@ -917,7 +897,7 @@ int transaction_add_job_and_dependencies(
* add all dependencies of everybody following. */
if (unit_following_set(ret->unit, &following) > 0) {
SET_FOREACH(dep, following, i) {
- r = transaction_add_job_and_dependencies(tr, type, dep, ret, false, override, false, false, ignore_order, e);
+ r = transaction_add_job_and_dependencies(tr, type, dep, ret, false, false, false, ignore_order, e);
if (r < 0) {
log_unit_warning(dep, "Cannot add dependency job for, ignoring: %s", bus_error_message(e, r));
sd_bus_error_free(e);
@@ -930,7 +910,7 @@ int transaction_add_job_and_dependencies(
/* Finally, recursively add in all dependencies. */
if (type == JOB_START || type == JOB_RESTART) {
SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES], i) {
- r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, override, false, false, ignore_order, e);
+ r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, false, false, ignore_order, e);
if (r < 0) {
if (r != -EBADR)
goto fail;
@@ -940,7 +920,7 @@ int transaction_add_job_and_dependencies(
}
SET_FOREACH(dep, ret->unit->dependencies[UNIT_BINDS_TO], i) {
- r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, override, false, false, ignore_order, e);
+ r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, false, false, ignore_order, e);
if (r < 0) {
if (r != -EBADR)
goto fail;
@@ -949,30 +929,20 @@ int transaction_add_job_and_dependencies(
}
}
- 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,
- 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);
+ r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, false, false, ignore_order, e);
if (r < 0) {
+ /* unit masked and unit not found are not considered as errors. */
log_unit_full(dep,
- r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, r,
- "Cannot add dependency job, ignoring: %s",
+ r == -EBADR || r == -ENOENT ? 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_REQUISITE], i) {
- r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, true, override, false, false, ignore_order, e);
+ r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, true, false, false, ignore_order, e);
if (r < 0) {
if (r != -EBADR)
goto fail;
@@ -981,19 +951,8 @@ int transaction_add_job_and_dependencies(
}
}
- 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,
- 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_CONFLICTS], i) {
- r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, true, override, true, false, ignore_order, e);
+ r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, true, true, false, ignore_order, e);
if (r < 0) {
if (r != -EBADR)
goto fail;
@@ -1003,7 +962,7 @@ int transaction_add_job_and_dependencies(
}
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);
+ r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, false, false, false, ignore_order, e);
if (r < 0) {
log_unit_warning(dep,
"Cannot add dependency job, ignoring: %s",
@@ -1038,7 +997,7 @@ int transaction_add_job_and_dependencies(
if (nt == JOB_NOP)
continue;
- r = transaction_add_job_and_dependencies(tr, nt, dep, ret, true, override, false, false, ignore_order, e);
+ r = transaction_add_job_and_dependencies(tr, nt, dep, ret, true, false, false, ignore_order, e);
if (r < 0) {
if (r != -EBADR)
goto fail;
@@ -1051,7 +1010,7 @@ int transaction_add_job_and_dependencies(
if (type == JOB_RELOAD) {
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);
+ r = transaction_add_job_and_dependencies(tr, JOB_RELOAD, dep, ret, false, false, false, ignore_order, e);
if (r < 0) {
log_unit_warning(dep,
"Cannot add dependency reload job, ignoring: %s",
@@ -1096,7 +1055,7 @@ int transaction_add_isolate_jobs(Transaction *tr, Manager *m) {
if (hashmap_get(tr->jobs, u))
continue;
- r = transaction_add_job_and_dependencies(tr, JOB_STOP, u, tr->anchor_job, true, false, false, false, false, NULL);
+ r = transaction_add_job_and_dependencies(tr, JOB_STOP, u, tr->anchor_job, true, false, false, false, NULL);
if (r < 0)
log_unit_warning_errno(u, r, "Cannot add isolate job, ignoring: %m");
}
diff --git a/src/core/transaction.h b/src/core/transaction.h
index d949b21b8d..5c4a13edab 100644
--- a/src/core/transaction.h
+++ b/src/core/transaction.h
@@ -23,10 +23,10 @@
typedef struct Transaction Transaction;
-#include "unit.h"
-#include "manager.h"
-#include "job.h"
#include "hashmap.h"
+#include "job.h"
+#include "manager.h"
+#include "unit.h"
struct Transaction {
/* Jobs to be added */
@@ -44,7 +44,6 @@ int transaction_add_job_and_dependencies(
Unit *unit,
Job *by,
bool matters,
- bool override,
bool conflicts,
bool ignore_requirements,
bool ignore_order,
diff --git a/src/core/triggers.systemd.in b/src/core/triggers.systemd.in
new file mode 100644
index 0000000000..9e18a39a67
--- /dev/null
+++ b/src/core/triggers.systemd.in
@@ -0,0 +1,64 @@
+# -*- Mode: rpm-spec; 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/>.
+
+# The contents of this are an example to be copied into systemd.spec.
+
+%transfiletriggerin -P 900900 -p <lua> -- @systemunitdir@ /etc/systemd/system
+-- This script will run after any package is initially installed or
+-- upgraded. We care about the case where a package is initially
+-- installed, because other cases are covered by the *un scriptlets,
+-- so sometimes we will reload needlessly.
+
+pid = posix.fork()
+if pid == 0 then
+ assert(posix.exec("%{_bindir}/systemctl", "daemon-reload"))
+elseif pid > 0 then
+ posix.wait(pid)
+end
+
+%transfiletriggerun -p <lua> -- @systemunitdir@ /etc/systemd/system
+-- On removal, we need to run daemon-reload after any units have been
+-- removed. %transfiletriggerpostun would be ideal, but it does not get
+-- executed for some reason.
+-- On upgrade, we need to run daemon-reload after any new unit files
+-- have been installed, but before %postun scripts in packages get
+-- executed. %transfiletriggerun gets the right list of files
+-- but it is invoked too early (before changes happen).
+-- %filetriggerpostun happens at the right time, but it fires for
+-- every package.
+-- To execute the reload at the right time, we create a state
+-- file in %transfiletriggerun and execute the daemon-reload in
+-- the first %filetriggerpostun.
+
+posix.mkdir("%{_localstatedir}/lib")
+posix.mkdir("%{_localstatedir}/lib/rpm-state")
+posix.mkdir("%{_localstatedir}/lib/rpm-state/systemd")
+io.open("%{_localstatedir}/lib/rpm-state/systemd/needs-reload", "w")
+
+%filetriggerpostun -P 1000100 -p <lua> -- @systemunitdir@ /etc/systemd/system
+if posix.access("%{_localstatedir}/lib/rpm-state/systemd/needs-reload") then
+ posix.unlink("%{_localstatedir}/lib/rpm-state/systemd/needs-reload")
+ posix.rmdir("%{_localstatedir}/lib/rpm-state/systemd")
+ pid = posix.fork()
+ if pid == 0 then
+ assert(posix.exec("%{_bindir}/systemctl", "daemon-reload"))
+ elseif pid > 0 then
+ posix.wait(pid)
+ end
+end
diff --git a/src/core/umount.c b/src/core/umount.c
index 22dbe67259..9d1f7660db 100644
--- a/src/core/umount.c
+++ b/src/core/umount.c
@@ -21,23 +21,30 @@
#include <errno.h>
#include <fcntl.h>
+#include <linux/dm-ioctl.h>
+#include <linux/loop.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/swap.h>
-#include <linux/loop.h>
-#include <linux/dm-ioctl.h>
+#include "libudev.h"
+
+#include "alloc-util.h"
+#include "escape.h"
+#include "fd-util.h"
+#include "fstab-util.h"
#include "list.h"
#include "mount-setup.h"
-#include "umount.h"
#include "path-util.h"
+#include "string-util.h"
+#include "udev-util.h"
+#include "umount.h"
#include "util.h"
#include "virt.h"
-#include "libudev.h"
-#include "udev-util.h"
typedef struct MountPoint {
char *path;
+ char *options;
dev_t devnum;
LIST_FIELDS(struct MountPoint, mount_point);
} MountPoint;
@@ -71,7 +78,7 @@ static int mount_points_list_get(MountPoint **head) {
return -errno;
for (i = 1;; i++) {
- _cleanup_free_ char *path = NULL;
+ _cleanup_free_ char *path = NULL, *options = NULL;
char *p = NULL;
MountPoint *m;
int k;
@@ -82,15 +89,15 @@ static int mount_points_list_get(MountPoint **head) {
"%*s " /* (3) major:minor */
"%*s " /* (4) root */
"%ms " /* (5) mount point */
- "%*s" /* (6) mount options */
+ "%*s" /* (6) mount flags */
"%*[^-]" /* (7) optional fields */
"- " /* (8) separator */
"%*s " /* (9) file system type */
"%*s" /* (10) mount source */
- "%*s" /* (11) mount options 2 */
+ "%ms" /* (11) mount options */
"%*[^\n]", /* some rubbish at the end */
- &path);
- if (k != 1) {
+ &path, &options);
+ if (k != 2) {
if (k == EOF)
break;
@@ -125,6 +132,9 @@ static int mount_points_list_get(MountPoint **head) {
}
m->path = p;
+ m->options = options;
+ options = NULL;
+
LIST_PREPEND(mount_point, *head, m);
}
@@ -369,6 +379,14 @@ static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_e
benefits, but might confuse the host, as we remount
the superblock here, not the bind mound. */
if (detect_container() <= 0) {
+ _cleanup_free_ char *options = NULL;
+ /* MS_REMOUNT requires that the data parameter
+ * should be the same from the original mount
+ * except for the desired changes. Since we want
+ * to remount read-only, we should filter out
+ * rw (and ro too, because it confuses the kernel) */
+ (void) fstab_filter_options(m->options, "rw\0ro\0", NULL, NULL, &options);
+
/* We always try to remount directories
* read-only first, before we go on and umount
* them.
@@ -385,7 +403,8 @@ static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_e
* alias read-only we hence should be
* relatively safe regarding keeping the fs we
* can otherwise not see dirty. */
- (void) mount(NULL, m->path, NULL, MS_REMOUNT|MS_RDONLY, NULL);
+ log_info("Remounting '%s' read-only with options '%s'.", m->path, options);
+ (void) mount(NULL, m->path, NULL, MS_REMOUNT|MS_RDONLY, options);
}
/* Skip / and /usr since we cannot unmount that
diff --git a/src/core/unit-printf.c b/src/core/unit-printf.c
index 0889769d03..f587a5a141 100644
--- a/src/core/unit-printf.c
+++ b/src/core/unit-printf.c
@@ -19,14 +19,17 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "unit.h"
+#include "alloc-util.h"
+#include "cgroup-util.h"
+#include "formats-util.h"
+#include "macro.h"
#include "specifier.h"
+#include "string-util.h"
#include "strv.h"
#include "unit-name.h"
#include "unit-printf.h"
-#include "macro.h"
-#include "cgroup-util.h"
-#include "formats-util.h"
+#include "unit.h"
+#include "user-util.h"
static int specifier_prefix_and_instance(char specifier, void *data, void *userdata, char **ret) {
Unit *u = userdata;
@@ -63,10 +66,7 @@ static int specifier_instance_unescaped(char specifier, void *data, void *userda
assert(u);
- if (!u->instance)
- return -EINVAL;
-
- return unit_name_unescape(u->instance, ret);
+ return unit_name_unescape(strempty(u->instance), ret);
}
static int specifier_filename(char specifier, void *data, void *userdata, char **ret) {
@@ -128,6 +128,8 @@ static int specifier_cgroup_slice(char specifier, void *data, void *userdata, ch
n = unit_default_cgroup_path(slice);
} else
n = strdup(u->manager->cgroup_root);
+ if (!n)
+ return -ENOMEM;
*ret = n;
return 0;
@@ -157,162 +159,43 @@ static int specifier_runtime(char specifier, void *data, void *userdata, char **
}
static int specifier_user_name(char specifier, void *data, void *userdata, char **ret) {
- char *printed = NULL;
- Unit *u = userdata;
- ExecContext *c;
- int r = 0;
-
- assert(u);
-
- c = unit_get_exec_context(u);
- if (!c)
- return -EINVAL;
-
- 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
- * it */
+ char *t;
- if (!c->user || streq(c->user, "root") || streq(c->user, "0"))
- printed = strdup(specifier == 'u' ? "root" : "0");
- else {
- if (specifier == 'u')
- printed = strdup(c->user);
- else {
- uid_t uid;
+ /* If we are UID 0 (root), this will not result in NSS,
+ * otherwise it might. This is good, as we want to be able to
+ * run this in PID 1, where our user ID is 0, but where NSS
+ * lookups are not allowed. */
- r = parse_uid(c->user, &uid);
- if (r < 0)
- return -ENODATA;
-
- r = asprintf(&printed, UID_FMT, uid);
- }
- }
-
- } else {
- _cleanup_free_ char *tmp = NULL;
- const char *username = NULL;
- uid_t uid;
-
- if (c->user)
- username = c->user;
- else
- /* get USER env from env or our own uid */
- username = tmp = getusername_malloc();
-
- /* fish username from passwd */
- r = get_user_creds(&username, &uid, NULL, NULL, NULL);
- if (r < 0)
- return r;
-
- if (specifier == 'u')
- printed = strdup(username);
- else
- r = asprintf(&printed, UID_FMT, uid);
- }
-
- if (r < 0 || !printed)
+ t = getusername_malloc();
+ if (!t)
return -ENOMEM;
- *ret = printed;
+ *ret = t;
return 0;
}
-static int specifier_user_home(char specifier, void *data, void *userdata, char **ret) {
- Unit *u = userdata;
- ExecContext *c;
- char *n;
- int r;
-
- assert(u);
-
- c = unit_get_exec_context(u);
- if (!c)
- return -EOPNOTSUPP;
-
- if (u->manager->running_as == MANAGER_SYSTEM) {
+static int specifier_user_id(char specifier, void *data, void *userdata, char **ret) {
- /* We cannot use NSS from PID 1, hence try to make the
- * best of it if we can, but fail if we can't */
-
- if (!c->user || streq(c->user, "root") || streq(c->user, "0"))
- n = strdup("/root");
- else
- return -EOPNOTSUPP;
-
- } else {
-
- /* return HOME if set, otherwise from passwd */
- if (!c || !c->user) {
- r = get_home_dir(&n);
- if (r < 0)
- return r;
- } else {
- const char *username, *home;
-
- username = c->user;
- r = get_user_creds(&username, NULL, NULL, &home, NULL);
- if (r < 0)
- return r;
-
- n = strdup(home);
- }
- }
-
- if (!n)
+ if (asprintf(ret, UID_FMT, getuid()) < 0)
return -ENOMEM;
- *ret = n;
return 0;
}
-static int specifier_user_shell(char specifier, void *data, void *userdata, char **ret) {
- Unit *u = userdata;
- ExecContext *c;
- char *n;
- int r;
-
- assert(u);
-
- c = unit_get_exec_context(u);
- if (!c)
- return -EOPNOTSUPP;
-
- 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 */
-
- if (!c->user || streq(c->user, "root") || streq(c->user, "0"))
- n = strdup("/bin/sh");
- else
- return -EOPNOTSUPP;
-
- } else {
+static int specifier_user_home(char specifier, void *data, void *userdata, char **ret) {
- /* return /bin/sh for root, otherwise the value from passwd */
- if (!c->user) {
- r = get_shell(&n);
- if (r < 0)
- return r;
- } else {
- const char *username, *shell;
+ /* On PID 1 (which runs as root) this will not result in NSS,
+ * which is good. See above */
- username = c->user;
- r = get_user_creds(&username, NULL, NULL, NULL, &shell);
- if (r < 0)
- return r;
+ return get_home_dir(ret);
+}
- n = strdup(shell);
- }
- }
+static int specifier_user_shell(char specifier, void *data, void *userdata, char **ret) {
- if (!n)
- return -ENOMEM;
+ /* On PID 1 (which runs as root) this will not result in NSS,
+ * which is good. See above */
- *ret = n;
- return 0;
+ return get_shell(ret);
}
int unit_name_printf(Unit *u, const char* format, char **ret) {
@@ -352,10 +235,10 @@ int unit_full_printf(Unit *u, const char *format, char **ret) {
* %r where units in this slice are placed in the cgroup tree
* %R the root of this systemd's instance tree
* %t the runtime directory to place sockets in (e.g. "/run" or $XDG_RUNTIME_DIR)
- * %U the UID of the configured user or running user
- * %u the username of the configured user or running user
- * %h the homedir of the configured user or running user
- * %s the shell of the configured user or running user
+ * %U the UID of the running user
+ * %u the username of the running user
+ * %h the homedir of the running user
+ * %s the shell of the running user
* %m the machine ID of the running system
* %H the host name of the running system
* %b the boot ID of the running system
@@ -375,7 +258,8 @@ int unit_full_printf(Unit *u, const char *format, char **ret) {
{ 'r', specifier_cgroup_slice, NULL },
{ 'R', specifier_cgroup_root, NULL },
{ 't', specifier_runtime, NULL },
- { 'U', specifier_user_name, NULL },
+
+ { 'U', specifier_user_id, NULL },
{ 'u', specifier_user_name, NULL },
{ 'h', specifier_user_home, NULL },
{ 's', specifier_user_shell, NULL },
diff --git a/src/core/unit.c b/src/core/unit.c
index 3a6313e4a2..32267d95f5 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -20,43 +20,50 @@
***/
#include <errno.h>
-#include <string.h>
#include <stdlib.h>
-#include <unistd.h>
+#include <string.h>
#include <sys/stat.h>
+#include <unistd.h>
#include "sd-id128.h"
#include "sd-messages.h"
-#include "set.h"
-#include "macro.h"
-#include "strv.h"
-#include "path-util.h"
-#include "log.h"
+
+#include "alloc-util.h"
+#include "bus-common-errors.h"
+#include "bus-util.h"
#include "cgroup-util.h"
-#include "missing.h"
-#include "mkdir.h"
+#include "dbus-unit.h"
+#include "dbus.h"
+#include "dropin.h"
+#include "escape.h"
+#include "execute.h"
#include "fileio-label.h"
#include "formats-util.h"
+#include "load-dropin.h"
+#include "load-fragment.h"
+#include "log.h"
+#include "macro.h"
+#include "missing.h"
+#include "mkdir.h"
+#include "parse-util.h"
+#include "path-util.h"
#include "process-util.h"
-#include "virt.h"
-#include "bus-common-errors.h"
-#include "bus-util.h"
-#include "dropin.h"
-#include "unit-name.h"
+#include "set.h"
#include "special.h"
+#include "stat-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "unit-name.h"
#include "unit.h"
-#include "load-fragment.h"
-#include "load-dropin.h"
-#include "dbus.h"
-#include "dbus-unit.h"
-#include "execute.h"
+#include "user-util.h"
+#include "virt.h"
const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = {
[UNIT_SERVICE] = &service_vtable,
[UNIT_SOCKET] = &socket_vtable,
[UNIT_BUSNAME] = &busname_vtable,
[UNIT_TARGET] = &target_vtable,
- [UNIT_SNAPSHOT] = &snapshot_vtable,
[UNIT_DEVICE] = &device_vtable,
[UNIT_MOUNT] = &mount_vtable,
[UNIT_AUTOMOUNT] = &automount_vtable,
@@ -126,6 +133,9 @@ static void unit_init(Unit *u) {
cc->blockio_accounting = u->manager->default_blockio_accounting;
cc->memory_accounting = u->manager->default_memory_accounting;
cc->tasks_accounting = u->manager->default_tasks_accounting;
+
+ if (u->type != UNIT_SLICE)
+ cc->tasks_max = u->manager->default_tasks_max;
}
ec = unit_get_exec_context(u);
@@ -308,9 +318,6 @@ bool unit_check_gc(Unit *u) {
if (state != UNIT_INACTIVE)
return true;
- if (UNIT_VTABLE(u)->no_gc)
- return true;
-
if (u->no_gc)
return true;
@@ -412,12 +419,11 @@ static void unit_remove_transient(Unit *u) {
STRV_FOREACH(i, u->dropin_paths) {
_cleanup_free_ char *p = NULL;
- int r;
(void) unlink(*i);
- r = path_get_parent(*i, &p);
- if (r >= 0)
+ p = dirname_malloc(*i);
+ if (p)
(void) rmdir(p);
}
}
@@ -992,15 +998,13 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
"%s\tRefuseManualStop: %s\n"
"%s\tDefaultDependencies: %s\n"
"%s\tOnFailureJobMode: %s\n"
- "%s\tIgnoreOnIsolate: %s\n"
- "%s\tIgnoreOnSnapshot: %s\n",
+ "%s\tIgnoreOnIsolate: %s\n",
prefix, yes_no(u->stop_when_unneeded),
prefix, yes_no(u->refuse_manual_start),
prefix, yes_no(u->refuse_manual_stop),
prefix, yes_no(u->default_dependencies),
prefix, job_mode_to_string(u->on_failure_job_mode),
- prefix, yes_no(u->ignore_on_isolate),
- prefix, yes_no(u->ignore_on_snapshot));
+ prefix, yes_no(u->ignore_on_isolate));
if (UNIT_VTABLE(u)->dump)
UNIT_VTABLE(u)->dump(u, f, prefix2);
@@ -1098,9 +1102,7 @@ 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
};
@@ -1129,12 +1131,12 @@ static int unit_add_slice_dependencies(Unit *u) {
return 0;
if (UNIT_ISSET(u->slice))
- return unit_add_two_dependencies(u, UNIT_AFTER, UNIT_WANTS, UNIT_DEREF(u->slice), true);
+ return unit_add_two_dependencies(u, UNIT_AFTER, UNIT_REQUIRES, UNIT_DEREF(u->slice), true);
- if (streq(u->id, SPECIAL_ROOT_SLICE))
+ if (unit_has_name(u, SPECIAL_ROOT_SLICE))
return 0;
- return unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_WANTS, SPECIAL_ROOT_SLICE, NULL, true);
+ return unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, SPECIAL_ROOT_SLICE, NULL, true);
}
static int unit_add_mount_dependencies(Unit *u) {
@@ -1147,13 +1149,23 @@ static int unit_add_mount_dependencies(Unit *u) {
char prefix[strlen(*i) + 1];
PATH_FOREACH_PREFIX_MORE(prefix, *i) {
+ _cleanup_free_ char *p = NULL;
Unit *m;
- r = manager_get_unit_by_path(u->manager, prefix, ".mount", &m);
+ r = unit_name_from_path(prefix, ".mount", &p);
if (r < 0)
return r;
- if (r == 0)
+
+ m = manager_get_unit(u->manager, p);
+ if (!m) {
+ /* Make sure to load the mount unit if
+ * it exists. If so the dependencies
+ * on this unit will be added later
+ * during the loading of the mount
+ * unit. */
+ (void) manager_load_unit_prepare(u->manager, p, NULL, NULL, &m);
continue;
+ }
if (m == u)
continue;
@@ -1336,12 +1348,18 @@ static bool unit_assert_test(Unit *u) {
return u->assert_result;
}
+void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) {
+ DISABLE_WARNING_FORMAT_NONLITERAL;
+ manager_status_printf(u->manager, STATUS_TYPE_NORMAL, status, unit_status_msg_format, unit_description(u));
+ REENABLE_WARNING;
+}
+
_pure_ static const char* unit_get_status_message_format(Unit *u, JobType t) {
const char *format;
const UnitStatusMessageFormats *format_table;
assert(u);
- assert(t == JOB_START || t == JOB_STOP || t == JOB_RELOAD);
+ assert(IN_SET(t, JOB_START, JOB_STOP, JOB_RELOAD));
if (t != JOB_RELOAD) {
format_table = &UNIT_VTABLE(u)->status_message_formats;
@@ -1366,6 +1384,10 @@ static void unit_status_print_starting_stopping(Unit *u, JobType t) {
assert(u);
+ /* Reload status messages have traditionally not been printed to console. */
+ if (!IN_SET(t, JOB_START, JOB_STOP))
+ return;
+
format = unit_get_status_message_format(u, t);
DISABLE_WARNING_FORMAT_NONLITERAL;
@@ -1380,7 +1402,7 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) {
assert(u);
- if (t != JOB_START && t != JOB_STOP && t != JOB_RELOAD)
+ if (!IN_SET(t, JOB_START, JOB_STOP, JOB_RELOAD))
return;
if (log_on_console())
@@ -1391,7 +1413,7 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) {
format = unit_get_status_message_format(u, t);
DISABLE_WARNING_FORMAT_NONLITERAL;
- snprintf(buf, sizeof(buf), format, unit_description(u));
+ xsprintf(buf, format, unit_description(u));
REENABLE_WARNING;
mid = t == JOB_START ? SD_MESSAGE_UNIT_STARTING :
@@ -1412,12 +1434,12 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) {
}
void unit_status_emit_starting_stopping_reloading(Unit *u, JobType t) {
+ assert(u);
+ assert(t >= 0);
+ assert(t < _JOB_TYPE_MAX);
unit_status_log_starting_stopping_reloading(u, t);
-
- /* Reload status messages have traditionally not been printed to console. */
- if (t != JOB_RELOAD)
- unit_status_print_starting_stopping(u, t);
+ unit_status_print_starting_stopping(u, t);
}
/* Errors:
@@ -1592,11 +1614,11 @@ bool unit_can_reload(Unit *u) {
static void unit_check_unneeded(Unit *u) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+
static const UnitDependency needed_dependencies[] = {
UNIT_REQUIRED_BY,
- UNIT_REQUIRED_BY_OVERRIDABLE,
UNIT_REQUISITE_OF,
- UNIT_REQUISITE_OF_OVERRIDABLE,
UNIT_WANTED_BY,
UNIT_BOUND_BY,
};
@@ -1633,12 +1655,13 @@ static void unit_check_unneeded(Unit *u) {
log_unit_info(u, "Unit not needed anymore. Stopping.");
/* Ok, nobody needs us anymore. Sniff. Then let's commit suicide */
- r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, true, NULL, NULL);
+ r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, &error, NULL);
if (r < 0)
- log_unit_warning_errno(u, r, "Failed to enqueue stop job, ignoring: %m");
+ log_unit_warning_errno(u, r, "Failed to enqueue stop job, ignoring: %s", bus_error_message(&error, r));
}
static void unit_check_binds_to(Unit *u) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
bool stop = false;
Unit *other;
Iterator i;
@@ -1678,9 +1701,9 @@ static void unit_check_binds_to(Unit *u) {
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. */
- r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, true, NULL, NULL);
+ r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, &error, NULL);
if (r < 0)
- log_unit_warning_errno(u, r, "Failed to enqueue stop job, ignoring: %m");
+ log_unit_warning_errno(u, r, "Failed to enqueue stop job, ignoring: %s", bus_error_message(&error, r));
}
static void retroactively_start_dependencies(Unit *u) {
@@ -1693,30 +1716,25 @@ static void retroactively_start_dependencies(Unit *u) {
SET_FOREACH(other, u->dependencies[UNIT_REQUIRES], i)
if (!set_get(u->dependencies[UNIT_AFTER], other) &&
!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
- manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL);
+ manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, NULL, NULL);
SET_FOREACH(other, u->dependencies[UNIT_BINDS_TO], i)
if (!set_get(u->dependencies[UNIT_AFTER], other) &&
!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
- manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL);
-
- SET_FOREACH(other, u->dependencies[UNIT_REQUIRES_OVERRIDABLE], i)
- if (!set_get(u->dependencies[UNIT_AFTER], other) &&
- !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
- manager_add_job(u->manager, JOB_START, other, JOB_FAIL, false, NULL, NULL);
+ manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, NULL, NULL);
SET_FOREACH(other, u->dependencies[UNIT_WANTS], i)
if (!set_get(u->dependencies[UNIT_AFTER], other) &&
!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
- manager_add_job(u->manager, JOB_START, other, JOB_FAIL, false, NULL, NULL);
+ manager_add_job(u->manager, JOB_START, other, JOB_FAIL, NULL, NULL);
SET_FOREACH(other, u->dependencies[UNIT_CONFLICTS], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
- manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL);
+ manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL);
SET_FOREACH(other, u->dependencies[UNIT_CONFLICTED_BY], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
- manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL);
+ manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL);
}
static void retroactively_stop_dependencies(Unit *u) {
@@ -1729,7 +1747,7 @@ static void retroactively_stop_dependencies(Unit *u) {
/* Pull down units which are bound to us recursively if enabled */
SET_FOREACH(other, u->dependencies[UNIT_BOUND_BY], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
- manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL);
+ manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL);
}
static void check_unneeded_dependencies(Unit *u) {
@@ -1743,18 +1761,12 @@ static void check_unneeded_dependencies(Unit *u) {
SET_FOREACH(other, u->dependencies[UNIT_REQUIRES], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
unit_check_unneeded(other);
- SET_FOREACH(other, u->dependencies[UNIT_REQUIRES_OVERRIDABLE], i)
- if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
- unit_check_unneeded(other);
SET_FOREACH(other, u->dependencies[UNIT_WANTS], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
unit_check_unneeded(other);
SET_FOREACH(other, u->dependencies[UNIT_REQUISITE], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
unit_check_unneeded(other);
- SET_FOREACH(other, u->dependencies[UNIT_REQUISITE_OVERRIDABLE], i)
- if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
- unit_check_unneeded(other);
SET_FOREACH(other, u->dependencies[UNIT_BINDS_TO], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
unit_check_unneeded(other);
@@ -1774,7 +1786,7 @@ void unit_start_on_failure(Unit *u) {
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);
+ r = manager_add_job(u->manager, JOB_START, other, u->on_failure_job_mode, NULL, NULL);
if (r < 0)
log_unit_error_errno(u, r, "Failed to enqueue OnFailure= job: %m");
}
@@ -2122,16 +2134,12 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_referen
static const UnitDependency inverse_table[_UNIT_DEPENDENCY_MAX] = {
[UNIT_REQUIRES] = UNIT_REQUIRED_BY,
- [UNIT_REQUIRES_OVERRIDABLE] = UNIT_REQUIRED_BY_OVERRIDABLE,
[UNIT_WANTS] = UNIT_WANTED_BY,
[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_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,
@@ -2310,47 +2318,9 @@ int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency
return unit_add_two_dependencies(u, d, e, other, add_reference);
}
-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;
-
- assert(u);
- assert(name || path);
-
- 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)
- return r;
-
- return unit_add_dependency(other, d, u, add_reference);
-}
-
-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;
-
- assert(u);
- assert(name || path);
-
- 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)
- return r;
-
- return unit_add_two_dependencies(other, d, e, u, add_reference);
-}
-
int set_unit_path(const char *p) {
/* This is mostly for debug purposes */
- if (setenv("SYSTEMD_UNIT_PATH", p, 0) < 0)
+ if (setenv("SYSTEMD_UNIT_PATH", p, 1) < 0)
return -errno;
return 0;
@@ -2498,26 +2468,23 @@ static int signal_name_owner_changed(sd_bus_message *message, void *userdata, sd
return 0;
}
-int unit_install_bus_match(sd_bus *bus, Unit *u, const char *name) {
- _cleanup_free_ char *match = NULL;
- Manager *m = u->manager;
+int unit_install_bus_match(Unit *u, sd_bus *bus, const char *name) {
+ const char *match;
- assert(m);
+ assert(u);
+ assert(bus);
+ assert(name);
if (u->match_bus_slot)
return -EBUSY;
- match = strjoin("type='signal',"
+ match = strjoina("type='signal',"
"sender='org.freedesktop.DBus',"
"path='/org/freedesktop/DBus',"
"interface='org.freedesktop.DBus',"
"member='NameOwnerChanged',"
- "arg0='",
- name,
- "'",
+ "arg0='", name, "'",
NULL);
- if (!match)
- return -ENOMEM;
return sd_bus_add_match(bus, &u->match_bus_slot, match, signal_name_owner_changed, u);
}
@@ -2534,9 +2501,9 @@ int unit_watch_bus_name(Unit *u, const char *name) {
if (u->manager->api_bus) {
/* If the bus is already available, install the match directly.
* Otherwise, just put the name in the list. bus_setup_api() will take care later. */
- r = unit_install_bus_match(u->manager->api_bus, u, name);
+ r = unit_install_bus_match(u, u->manager->api_bus, name);
if (r < 0)
- return log_warning_errno(r, "Failed to subscribe to NameOwnerChanged signal: %m");
+ return log_warning_errno(r, "Failed to subscribe to NameOwnerChanged signal for '%s': %m", name);
}
r = hashmap_put(u->manager->watch_bus, name, u);
@@ -2624,6 +2591,62 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
return 0;
}
+int unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value) {
+ assert(u);
+ assert(f);
+ assert(key);
+
+ if (!value)
+ return 0;
+
+ fputs(key, f);
+ fputc('=', f);
+ fputs(value, f);
+ fputc('\n', f);
+
+ return 1;
+}
+
+int unit_serialize_item_escaped(Unit *u, FILE *f, const char *key, const char *value) {
+ _cleanup_free_ char *c = NULL;
+
+ assert(u);
+ assert(f);
+ assert(key);
+
+ if (!value)
+ return 0;
+
+ c = cescape(value);
+ if (!c)
+ return -ENOMEM;
+
+ fputs(key, f);
+ fputc('=', f);
+ fputs(c, f);
+ fputc('\n', f);
+
+ return 1;
+}
+
+int unit_serialize_item_fd(Unit *u, FILE *f, FDSet *fds, const char *key, int fd) {
+ int copy;
+
+ assert(u);
+ assert(f);
+ assert(key);
+
+ if (fd < 0)
+ return 0;
+
+ copy = fdset_put_dup(fds, fd);
+ if (copy < 0)
+ return copy;
+
+ fprintf(f, "%s=%i\n", key, copy);
+ return 1;
+}
+
void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *format, ...) {
va_list ap;
@@ -2642,15 +2665,6 @@ void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *f
fputc('\n', f);
}
-void unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value) {
- assert(u);
- assert(f);
- assert(key);
- assert(value);
-
- fprintf(f, "%s=%s\n", key, value);
-}
-
int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
ExecRuntime **rt = NULL;
size_t offset;
@@ -2827,7 +2841,7 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
}
}
-int unit_add_node_link(Unit *u, const char *what, bool wants) {
+int unit_add_node_link(Unit *u, const char *what, bool wants, UnitDependency dep) {
Unit *device;
_cleanup_free_ char *e = NULL;
int r;
@@ -2854,7 +2868,9 @@ int unit_add_node_link(Unit *u, const char *what, bool wants) {
if (r < 0)
return r;
- r = unit_add_two_dependencies(u, UNIT_AFTER, u->manager->running_as == MANAGER_SYSTEM ? UNIT_BINDS_TO : UNIT_WANTS, device, true);
+ r = unit_add_two_dependencies(u, UNIT_AFTER,
+ u->manager->running_as == MANAGER_SYSTEM ? dep : UNIT_WANTS,
+ device, true);
if (r < 0)
return r;
@@ -2868,7 +2884,7 @@ int unit_add_node_link(Unit *u, const char *what, bool wants) {
}
int unit_coldplug(Unit *u) {
- int r;
+ int r = 0, q = 0;
assert(u);
@@ -2879,26 +2895,18 @@ int unit_coldplug(Unit *u) {
u->coldplugged = true;
- if (UNIT_VTABLE(u)->coldplug) {
+ 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;
- }
+ if (u->job)
+ q = job_coldplug(u->job);
- return 0;
-}
+ if (r < 0)
+ return r;
+ if (q < 0)
+ return q;
-void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) {
- DISABLE_WARNING_FORMAT_NONLITERAL;
- manager_status_printf(u->manager, STATUS_TYPE_NORMAL,
- status, unit_status_msg_format, unit_description(u));
- REENABLE_WARNING;
+ return 0;
}
bool unit_need_daemon_reload(Unit *u) {
@@ -3112,7 +3120,7 @@ int unit_kill_common(
killed = true;
}
- if (r == 0 && !killed && IN_SET(who, KILL_ALL_FAIL, KILL_CONTROL_FAIL, KILL_ALL_FAIL))
+ if (r == 0 && !killed && IN_SET(who, KILL_ALL_FAIL, KILL_CONTROL_FAIL))
return -ESRCH;
return r;
@@ -3130,12 +3138,19 @@ int unit_following_set(Unit *u, Set **s) {
}
UnitFileState unit_get_unit_file_state(Unit *u) {
+ int r;
+
assert(u);
- if (u->unit_file_state < 0 && u->fragment_path)
- u->unit_file_state = unit_file_get_state(
+ if (u->unit_file_state < 0 && u->fragment_path) {
+ r = unit_file_get_state(
u->manager->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER,
- NULL, basename(u->fragment_path));
+ NULL,
+ basename(u->fragment_path),
+ &u->unit_file_state);
+ if (r < 0)
+ u->unit_file_state = UNIT_FILE_BAD;
+ }
return u->unit_file_state;
}
@@ -3146,7 +3161,8 @@ 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 == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER,
- NULL, basename(u->fragment_path));
+ NULL,
+ basename(u->fragment_path));
return u->unit_file_preset;
}
@@ -3216,7 +3232,7 @@ int unit_patch_contexts(Unit *u) {
ec->no_new_privileges = true;
if (ec->private_devices)
- ec->capability_bounding_set_drop |= (uint64_t) 1ULL << (uint64_t) CAP_MKNOD;
+ ec->capability_bounding_set &= ~(UINT64_C(1) << CAP_MKNOD);
}
cc = unit_get_cgroup_context(u);
@@ -3311,19 +3327,6 @@ static int unit_drop_in_dir(Unit *u, UnitSetPropertiesMode mode, bool transient,
return 0;
}
-static int unit_drop_in_file(Unit *u, UnitSetPropertiesMode mode, const char *name, char **p, char **q) {
- _cleanup_free_ char *dir = NULL;
- int r;
-
- assert(u);
-
- r = unit_drop_in_dir(u, mode, u->transient, &dir);
- if (r < 0)
- return r;
-
- return drop_in_file(dir, u->id, 50, name, p, q);
-}
-
int unit_write_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data) {
_cleanup_free_ char *dir = NULL, *p = NULL, *q = NULL;
@@ -3422,28 +3425,6 @@ int unit_write_drop_in_private_format(Unit *u, UnitSetPropertiesMode mode, const
return unit_write_drop_in_private(u, mode, name, p);
}
-int unit_remove_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name) {
- _cleanup_free_ char *p = NULL, *q = NULL;
- int r;
-
- assert(u);
-
- if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME))
- return 0;
-
- r = unit_drop_in_file(u, mode, name, &p, &q);
- if (r < 0)
- return r;
-
- if (unlink(q) < 0)
- r = errno == ENOENT ? 0 : -errno;
- else
- r = 1;
-
- rmdir(p);
- return r;
-}
-
int unit_make_transient(Unit *u) {
assert(u);
@@ -3453,7 +3434,15 @@ int unit_make_transient(Unit *u) {
u->load_state = UNIT_STUB;
u->load_error = 0;
u->transient = true;
+
u->fragment_path = mfree(u->fragment_path);
+ u->source_path = mfree(u->source_path);
+ u->dropin_paths = strv_free(u->dropin_paths);
+ u->fragment_mtime = u->source_mtime = u->dropin_mtime = 0;
+
+ unit_add_to_dbus_queue(u);
+ unit_add_to_gc_queue(u);
+ unit_add_to_load_queue(u);
return 0;
}
@@ -3730,13 +3719,20 @@ int unit_fail_if_symlink(Unit *u, const char* where) {
return -ELOOP;
}
-static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = {
- [UNIT_ACTIVE] = "active",
- [UNIT_RELOADING] = "reloading",
- [UNIT_INACTIVE] = "inactive",
- [UNIT_FAILED] = "failed",
- [UNIT_ACTIVATING] = "activating",
- [UNIT_DEACTIVATING] = "deactivating"
-};
+bool unit_is_pristine(Unit *u) {
+ assert(u);
+
+ /* Check if the unit already exists or is already around,
+ * in a number of different ways. Note that to cater for unit
+ * types such as slice, we are generally fine with units that
+ * are marked UNIT_LOADED even even though nothing was
+ * actually loaded, as those unit types don't require a file
+ * on disk to validly load. */
-DEFINE_STRING_TABLE_LOOKUP(unit_active_state, UnitActiveState);
+ return !(!IN_SET(u->load_state, UNIT_NOT_FOUND, UNIT_LOADED) ||
+ u->fragment_path ||
+ u->source_path ||
+ !strv_isempty(u->dropin_paths) ||
+ u->job ||
+ u->merged_into);
+}
diff --git a/src/core/unit.h b/src/core/unit.h
index c868d75c79..3eb3484fb7 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -27,26 +27,14 @@
typedef struct Unit Unit;
typedef struct UnitVTable UnitVTable;
-typedef enum UnitActiveState UnitActiveState;
typedef struct UnitRef UnitRef;
typedef struct UnitStatusMessageFormats UnitStatusMessageFormats;
-#include "list.h"
#include "condition.h"
+#include "failure-action.h"
#include "install.h"
+#include "list.h"
#include "unit-name.h"
-#include "failure-action.h"
-
-enum UnitActiveState {
- UNIT_ACTIVE,
- UNIT_RELOADING,
- UNIT_INACTIVE,
- UNIT_FAILED,
- UNIT_ACTIVATING,
- UNIT_DEACTIVATING,
- _UNIT_ACTIVE_STATE_MAX,
- _UNIT_ACTIVE_STATE_INVALID = -1
-};
typedef enum KillOperation {
KILL_TERMINATE,
@@ -215,9 +203,6 @@ struct Unit {
/* Ignore this unit when isolating */
bool ignore_on_isolate;
- /* Ignore this unit when snapshotting */
- bool ignore_on_snapshot;
-
/* Did the last condition check succeed? */
bool condition_result;
bool assert_result;
@@ -257,17 +242,16 @@ typedef enum UnitSetPropertiesMode {
UNIT_PERSISTENT = 2,
} UnitSetPropertiesMode;
-#include "socket.h"
+#include "automount.h"
#include "busname.h"
-#include "target.h"
-#include "snapshot.h"
#include "device.h"
-#include "automount.h"
-#include "swap.h"
-#include "timer.h"
-#include "slice.h"
#include "path.h"
#include "scope.h"
+#include "slice.h"
+#include "socket.h"
+#include "swap.h"
+#include "target.h"
+#include "timer.h"
struct UnitVTable {
/* How much memory does an object of this unit type need */
@@ -334,7 +318,7 @@ struct UnitVTable {
int (*deserialize_item)(Unit *u, const char *key, const char *data, FDSet *fds);
/* Try to match up fds with what we need for this unit */
- int (*distribute_fds)(Unit *u, FDSet *fds);
+ void (*distribute_fds)(Unit *u, FDSet *fds);
/* Boils down the more complex internal state of this unit to
* a simpler one that the engine can understand */
@@ -355,9 +339,6 @@ struct UnitVTable {
* shall release its runtime resources */
void (*release_resources)(Unit *u);
- /* Return true when this unit is suitable for snapshotting */
- bool (*check_snapshot)(Unit *u);
-
/* Invoked on every child that died */
void (*sigchld_event)(Unit *u, pid_t pid, int code, int status);
@@ -401,7 +382,7 @@ struct UnitVTable {
* everything that is loaded here should still stay in
* inactive state. It is the job of the coldplug() call above
* to put the units into the initial state. */
- int (*enumerate)(Manager *m);
+ void (*enumerate)(Manager *m);
/* Type specific cleanups. */
void (*shutdown)(Manager *m);
@@ -422,9 +403,6 @@ struct UnitVTable {
/* Instances make no sense for this type */
bool no_instances:1;
- /* Exclude from automatic gc */
- bool no_gc:1;
-
/* True if transient units of this type are OK */
bool can_transient:1;
};
@@ -455,7 +433,6 @@ DEFINE_CAST(SERVICE, Service);
DEFINE_CAST(SOCKET, Socket);
DEFINE_CAST(BUSNAME, BusName);
DEFINE_CAST(TARGET, Target);
-DEFINE_CAST(SNAPSHOT, Snapshot);
DEFINE_CAST(DEVICE, Device);
DEFINE_CAST(MOUNT, Mount);
DEFINE_CAST(AUTOMOUNT, Automount);
@@ -476,9 +453,6 @@ int unit_add_two_dependencies(Unit *u, UnitDependency d, UnitDependency e, Unit
int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, const char *filename, bool add_reference);
int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference);
-int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *name, const char *filename, bool add_reference);
-int unit_add_two_dependencies_by_name_inverse(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference);
-
int unit_add_exec_dependencies(Unit *u, ExecContext *c);
int unit_choose_id(Unit *u, const char *name);
@@ -532,7 +506,7 @@ void unit_unwatch_all_pids(Unit *u);
void unit_tidy_watch_pids(Unit *u, pid_t except1, pid_t except2);
-int unit_install_bus_match(sd_bus *bus, Unit *u, const char *name);
+int unit_install_bus_match(Unit *u, sd_bus *bus, const char *name);
int unit_watch_bus_name(Unit *u, const char *name);
void unit_unwatch_bus_name(Unit *u, const char *name);
@@ -545,12 +519,16 @@ char *unit_dbus_path(Unit *u);
int unit_load_related_unit(Unit *u, const char *type, Unit **_found);
bool unit_can_serialize(Unit *u) _pure_;
+
int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs);
-void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *value, ...) _printf_(4,5);
-void unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value);
int unit_deserialize(Unit *u, FILE *f, FDSet *fds);
-int unit_add_node_link(Unit *u, const char *what, bool wants);
+int unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value);
+int unit_serialize_item_escaped(Unit *u, FILE *f, const char *key, const char *value);
+int unit_serialize_item_fd(Unit *u, FILE *f, FDSet *fds, const char *key, int fd);
+void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *value, ...) _printf_(4,5);
+
+int unit_add_node_link(Unit *u, const char *what, bool wants, UnitDependency d);
int unit_coldplug(Unit *u);
@@ -600,8 +578,6 @@ int unit_write_drop_in_format(Unit *u, UnitSetPropertiesMode mode, const char *n
int unit_write_drop_in_private(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data);
int unit_write_drop_in_private_format(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *format, ...) _printf_(4,5);
-int unit_remove_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name);
-
int unit_kill_context(Unit *u, KillContext *c, KillOperation k, pid_t main_pid, pid_t control_pid, bool main_pid_alien);
int unit_make_transient(Unit *u);
@@ -610,6 +586,8 @@ int unit_require_mounts_for(Unit *u, const char *path);
bool unit_type_supported(UnitType t);
+bool unit_is_pristine(Unit *u);
+
static inline bool unit_supported(Unit *u) {
return unit_type_supported(u->type);
}
@@ -617,9 +595,6 @@ static inline bool unit_supported(Unit *u) {
void unit_warn_if_dir_nonempty(Unit *u, const char* where);
int unit_fail_if_symlink(Unit *u, const char* where);
-const char *unit_active_state_to_string(UnitActiveState i) _const_;
-UnitActiveState unit_active_state_from_string(const char *s) _pure_;
-
/* Macros which append UNIT= or USER_UNIT= to the message */
#define log_unit_full(unit, level, error, ...) \
diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c
index ab91afec4d..ae53bac600 100644
--- a/src/cryptsetup/cryptsetup-generator.c
+++ b/src/cryptsetup/cryptsetup-generator.c
@@ -21,13 +21,19 @@
#include <errno.h>
+#include "alloc-util.h"
#include "dropin.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fstab-util.h"
#include "generator.h"
#include "hashmap.h"
#include "log.h"
#include "mkdir.h"
+#include "parse-util.h"
#include "path-util.h"
-#include "fstab-util.h"
+#include "proc-cmdline.h"
+#include "string-util.h"
#include "strv.h"
#include "unit-name.h"
#include "util.h"
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
index 5d5872b7f4..bc708bca67 100644
--- a/src/cryptsetup/cryptsetup.c
+++ b/src/cryptsetup/cryptsetup.c
@@ -19,21 +19,26 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <string.h>
#include <errno.h>
-#include <sys/mman.h>
+#include <libcryptsetup.h>
#include <mntent.h>
+#include <string.h>
+#include <sys/mman.h>
-#include <libcryptsetup.h>
+#include "sd-device.h"
+#include "alloc-util.h"
+#include "ask-password-api.h"
+#include "device-util.h"
+#include "escape.h"
#include "fileio.h"
#include "log.h"
-#include "util.h"
+#include "mount-util.h"
+#include "parse-util.h"
#include "path-util.h"
+#include "string-util.h"
#include "strv.h"
-#include "ask-password-api.h"
-#include "sd-device.h"
-#include "device-util.h"
+#include "util.h"
static const char *arg_type = NULL; /* CRYPT_LUKS1, CRYPT_TCRYPT or CRYPT_PLAIN */
static char *arg_cipher = NULL;
@@ -263,7 +268,7 @@ static char* disk_description(const char *path) {
"ID_MODEL_FROM_DATABASE\0"
"ID_MODEL\0";
- _cleanup_device_unref_ sd_device *device = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
struct stat st;
const char *i;
int r;
@@ -312,19 +317,16 @@ static char *disk_mount_point(const char *label) {
return NULL;
}
-static int get_password(const char *vol, const char *src, usec_t until, bool accept_cached, char ***passwords) {
- int r = 0;
- char **p;
- _cleanup_free_ char *text = NULL;
- _cleanup_free_ char *escaped_name = NULL;
- char *id;
+static int get_password(const char *vol, const char *src, usec_t until, bool accept_cached, char ***ret) {
+ _cleanup_free_ char *description = NULL, *name_buffer = NULL, *mount_point = NULL, *maj_min = NULL, *text = NULL, *escaped_name = NULL;
+ _cleanup_strv_free_erase_ char **passwords = NULL;
const char *name = NULL;
- _cleanup_free_ char *description = NULL, *name_buffer = NULL,
- *mount_point = NULL, *maj_min = NULL;
+ char **p, *id;
+ int r = 0;
assert(vol);
assert(src);
- assert(passwords);
+ assert(ret);
description = disk_description(src);
mount_point = disk_mount_point(vol);
@@ -364,42 +366,45 @@ static int get_password(const char *vol, const char *src, usec_t until, bool acc
id = strjoina("cryptsetup:", escaped_name);
- r = ask_password_auto(text, "drive-harddisk", id, until, accept_cached, passwords);
+ r = ask_password_auto(text, "drive-harddisk", id, "cryptsetup", until,
+ ASK_PASSWORD_PUSH_CACHE | (accept_cached*ASK_PASSWORD_ACCEPT_CACHED),
+ &passwords);
if (r < 0)
return log_error_errno(r, "Failed to query password: %m");
if (arg_verify) {
- _cleanup_strv_free_ char **passwords2 = NULL;
+ _cleanup_strv_free_erase_ char **passwords2 = NULL;
- assert(strv_length(*passwords) == 1);
+ assert(strv_length(passwords) == 1);
if (asprintf(&text, "Please enter passphrase for disk %s! (verification)", name) < 0)
return log_oom();
id = strjoina("cryptsetup-verification:", escaped_name);
- r = ask_password_auto(text, "drive-harddisk", id, until, false, &passwords2);
+ r = ask_password_auto(text, "drive-harddisk", id, "cryptsetup", until, ASK_PASSWORD_PUSH_CACHE, &passwords2);
if (r < 0)
return log_error_errno(r, "Failed to query verification password: %m");
assert(strv_length(passwords2) == 1);
- if (!streq(*passwords[0], passwords2[0])) {
+ if (!streq(passwords[0], passwords2[0])) {
log_warning("Passwords did not match, retrying.");
return -EAGAIN;
}
}
- strv_uniq(*passwords);
+ strv_uniq(passwords);
- STRV_FOREACH(p, *passwords) {
+ STRV_FOREACH(p, passwords) {
char *c;
if (strlen(*p)+1 >= arg_key_size)
continue;
/* Pad password if necessary */
- if (!(c = new(char, arg_key_size)))
+ c = new(char, arg_key_size);
+ if (!c)
return log_oom();
strncpy(c, *p, arg_key_size);
@@ -407,14 +412,19 @@ static int get_password(const char *vol, const char *src, usec_t until, bool acc
*p = c;
}
+ *ret = passwords;
+ passwords = NULL;
+
return 0;
}
-static int attach_tcrypt(struct crypt_device *cd,
- const char *name,
- const char *key_file,
- char **passwords,
- uint32_t flags) {
+static int attach_tcrypt(
+ struct crypt_device *cd,
+ const char *name,
+ const char *key_file,
+ char **passwords,
+ uint32_t flags) {
+
int r = 0;
_cleanup_free_ char *passphrase = NULL;
struct crypt_params_tcrypt params = {
@@ -524,8 +534,7 @@ static int attach_luks_or_plain(struct crypt_device *cd,
* it just configures encryption
* parameters when used for plain
* mode. */
- r = crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode,
- NULL, NULL, arg_keyfile_size, &params);
+ r = crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, arg_keyfile_size, &params);
/* hash == NULL implies the user passed "plain" */
pass_volume_key = (params.hash == NULL);
@@ -541,9 +550,7 @@ static int attach_luks_or_plain(struct crypt_device *cd,
crypt_get_device_name(cd));
if (key_file) {
- r = crypt_activate_by_keyfile_offset(cd, name, arg_key_slot,
- key_file, arg_keyfile_size,
- arg_keyfile_offset, flags);
+ r = crypt_activate_by_keyfile_offset(cd, name, arg_key_slot, key_file, arg_keyfile_size, arg_keyfile_offset, flags);
if (r < 0) {
log_error_errno(r, "Failed to activate with key file '%s': %m", key_file);
return -EAGAIN;
@@ -635,7 +642,6 @@ int main(int argc, char *argv[]) {
k = crypt_init(&cd, arg_header);
} else
k = crypt_init(&cd, argv[3]);
-
if (k) {
log_error_errno(k, "crypt_init() failed: %m");
goto finish;
@@ -673,7 +679,7 @@ int main(int argc, char *argv[]) {
}
for (tries = 0; arg_tries == 0 || tries < arg_tries; tries++) {
- _cleanup_strv_free_ char **passwords = NULL;
+ _cleanup_strv_free_erase_ char **passwords = NULL;
if (!key_file) {
k = get_password(argv[2], argv[3], until, tries == 0 && !arg_verify, &passwords);
diff --git a/src/dbus1-generator/dbus1-generator.c b/src/dbus1-generator/dbus1-generator.c
index 7bbec5467e..6861a592fe 100644
--- a/src/dbus1-generator/dbus1-generator.c
+++ b/src/dbus1-generator/dbus1-generator.c
@@ -19,14 +19,18 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
+#include "alloc-util.h"
+#include "bus-internal.h"
+#include "bus-util.h"
+#include "cgroup-util.h"
#include "conf-parser.h"
-#include "special.h"
+#include "dirent-util.h"
+#include "fd-util.h"
+#include "fileio.h"
#include "mkdir.h"
-#include "bus-util.h"
-#include "bus-internal.h"
+#include "special.h"
#include "unit-name.h"
-#include "cgroup-util.h"
+#include "util.h"
static const char *arg_dest_late = "/tmp", *arg_dest = "/tmp";
@@ -223,8 +227,7 @@ static int parse_dbus_fragments(const char *path, const char *type) {
if (errno == -ENOENT)
return 0;
- log_error_errno(errno, "Failed to enumerate D-Bus activated services: %m");
- return -errno;
+ return log_error_errno(errno, "Failed to enumerate D-Bus activated services: %m");
}
r = 0;
@@ -242,8 +245,7 @@ static int parse_dbus_fragments(const char *path, const char *type) {
return r;
fail:
- log_error_errno(errno, "Failed to read D-Bus services directory: %m");
- return -errno;
+ return log_error_errno(errno, "Failed to read D-Bus services directory: %m");
}
static int link_busnames_target(const char *units) {
diff --git a/src/debug-generator/debug-generator.c b/src/debug-generator/debug-generator.c
index 8b29e8fd09..413cfd0388 100644
--- a/src/debug-generator/debug-generator.c
+++ b/src/debug-generator/debug-generator.c
@@ -19,11 +19,17 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
+#include "alloc-util.h"
+#include "mkdir.h"
+#include "parse-util.h"
+#include "proc-cmdline.h"
+#include "special.h"
+#include "string-util.h"
#include "strv.h"
#include "unit-name.h"
-#include "mkdir.h"
+#include "util.h"
+static char *arg_default_unit = NULL;
static const char *arg_dest = "/tmp";
static char **arg_mask = NULL;
static char **arg_wants = NULL;
@@ -76,6 +82,24 @@ static int parse_proc_cmdline_item(const char *key, const char *value) {
arg_debug_shell = r;
} else
arg_debug_shell = true;
+ } else if (streq(key, "systemd.unit")) {
+
+ if (!value)
+ log_error("Missing argument for systemd.unit= kernel command line parameter.");
+ else {
+ r = free_and_strdup(&arg_default_unit, value);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set default unit %s: %m", value);
+ }
+ } else if (!value) {
+ const char *target;
+
+ target = runlevel_to_target(key);
+ if (target) {
+ r = free_and_strdup(&arg_default_unit, target);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set default unit %s: %m", target);
+ }
}
return 0;
@@ -114,7 +138,7 @@ static int generate_wants_symlinks(void) {
STRV_FOREACH(u, arg_wants) {
_cleanup_free_ char *p = NULL, *f = NULL;
- p = strjoin(arg_dest, "/default.target.wants/", *u, NULL);
+ p = strjoin(arg_dest, "/", arg_default_unit, ".wants/", *u, NULL);
if (!p)
return log_oom();
@@ -150,6 +174,12 @@ int main(int argc, char *argv[]) {
umask(0022);
+ r = free_and_strdup(&arg_default_unit, SPECIAL_DEFAULT_TARGET);
+ if (r < 0) {
+ log_error_errno(r, "Failed to set default unit %s: %m", SPECIAL_DEFAULT_TARGET);
+ goto finish;
+ }
+
r = parse_proc_cmdline(parse_proc_cmdline_item);
if (r < 0)
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
diff --git a/src/delta/delta.c b/src/delta/delta.c
index 990130d00b..8bf678c28f 100644
--- a/src/delta/delta.c
+++ b/src/delta/delta.c
@@ -21,21 +21,28 @@
***/
#include <errno.h>
-#include <string.h>
-#include <unistd.h>
#include <getopt.h>
+#include <string.h>
#include <sys/prctl.h>
+#include <unistd.h>
+#include "alloc-util.h"
+#include "dirent-util.h"
+#include "fd-util.h"
+#include "fs-util.h"
#include "hashmap.h"
-#include "util.h"
-#include "path-util.h"
+#include "locale-util.h"
#include "log.h"
#include "pager.h"
-#include "build.h"
-#include "strv.h"
+#include "parse-util.h"
+#include "path-util.h"
#include "process-util.h"
-#include "terminal-util.h"
#include "signal-util.h"
+#include "stat-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "terminal-util.h"
+#include "util.h"
static const char prefixes[] =
"/etc\0"
@@ -312,8 +319,7 @@ static int enumerate_dir(Hashmap *top, Hashmap *bottom, Hashmap *drops, const ch
if (errno == ENOENT)
return 0;
- log_error_errno(errno, "Failed to open %s: %m", path);
- return -errno;
+ return log_error_errno(errno, "Failed to open %s: %m", path);
}
for (;;) {
@@ -544,9 +550,7 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case ARG_NO_PAGER:
arg_no_pager = true;
diff --git a/src/detect-virt/detect-virt.c b/src/detect-virt/detect-virt.c
index 97ae569ca5..0a256c29be 100644
--- a/src/detect-virt/detect-virt.c
+++ b/src/detect-virt/detect-virt.c
@@ -19,20 +19,20 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
-#include <stdbool.h>
#include <errno.h>
#include <getopt.h>
+#include <stdbool.h>
+#include <stdlib.h>
#include "util.h"
#include "virt.h"
-#include "build.h"
static bool arg_quiet = false;
static enum {
ANY_VIRTUALIZATION,
ONLY_VM,
- ONLY_CONTAINER
+ ONLY_CONTAINER,
+ ONLY_CHROOT,
} arg_mode = ANY_VIRTUALIZATION;
static void help(void) {
@@ -42,6 +42,7 @@ static void help(void) {
" --version Show package version\n"
" -c --container Only detect whether we are run in a container\n"
" -v --vm Only detect whether we are run in a VM\n"
+ " -r --chroot Detect whether we are run in a chroot() environment\n"
" -q --quiet Don't output anything, just set return value\n"
, program_invocation_short_name);
}
@@ -56,7 +57,8 @@ static int parse_argv(int argc, char *argv[]) {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "container", no_argument, NULL, 'c' },
- { "vm", optional_argument, NULL, 'v' },
+ { "vm", no_argument, NULL, 'v' },
+ { "chroot", no_argument, NULL, 'r' },
{ "quiet", no_argument, NULL, 'q' },
{}
};
@@ -66,7 +68,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "hqcv", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "hqcvr", options, NULL)) >= 0)
switch (c) {
@@ -75,9 +77,7 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case 'q':
arg_quiet = true;
@@ -91,6 +91,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_mode = ONLY_VM;
break;
+ case 'r':
+ arg_mode = ONLY_CHROOT;
+ break;
+
case '?':
return -EINVAL;
@@ -99,8 +103,7 @@ static int parse_argv(int argc, char *argv[]) {
}
if (optind < argc) {
- log_error("%s takes no arguments.",
- program_invocation_short_name);
+ log_error("%s takes no arguments.", program_invocation_short_name);
return -EINVAL;
}
@@ -108,7 +111,7 @@ static int parse_argv(int argc, char *argv[]) {
}
int main(int argc, char *argv[]) {
- int retval = EXIT_SUCCESS, r;
+ int r;
/* This is mostly intended to be used for scripts which want
* to detect whether we are being run in a virtualized
@@ -126,7 +129,7 @@ int main(int argc, char *argv[]) {
case ONLY_VM:
r = detect_vm();
if (r < 0) {
- log_error_errno(r, "Failed to check for vm: %m");
+ log_error_errno(r, "Failed to check for VM: %m");
return EXIT_FAILURE;
}
@@ -141,6 +144,15 @@ int main(int argc, char *argv[]) {
break;
+ case ONLY_CHROOT:
+ r = running_in_chroot();
+ if (r < 0) {
+ log_error_errno(r, "Failed to check for chroot() environment: %m");
+ return EXIT_FAILURE;
+ }
+
+ return r ? EXIT_SUCCESS : EXIT_FAILURE;
+
case ANY_VIRTUALIZATION:
default:
r = detect_virtualization();
@@ -155,7 +167,5 @@ int main(int argc, char *argv[]) {
if (!arg_quiet)
puts(virtualization_to_string(r));
- retval = r != VIRTUALIZATION_NONE ? EXIT_SUCCESS : EXIT_FAILURE;
-
- return retval;
+ return r != VIRTUALIZATION_NONE ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/src/escape/escape.c b/src/escape/escape.c
index 341453398d..e857affbc4 100644
--- a/src/escape/escape.c
+++ b/src/escape/escape.c
@@ -19,14 +19,15 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
-#include <getopt.h>
+#include "alloc-util.h"
#include "log.h"
-#include "unit-name.h"
-#include "build.h"
+#include "string-util.h"
#include "strv.h"
+#include "unit-name.h"
static enum {
ACTION_ESCAPE,
@@ -83,9 +84,7 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case ARG_SUFFIX:
diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c
index e2a1c00a75..cc5e9741fe 100644
--- a/src/firstboot/firstboot.c
+++ b/src/firstboot/firstboot.c
@@ -19,24 +19,29 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-
#include <fcntl.h>
-#include <unistd.h>
#include <getopt.h>
#include <shadow.h>
+#include <unistd.h>
-#include "strv.h"
-#include "fileio.h"
+#include "alloc-util.h"
+#include "ask-password-api.h"
#include "copy.h"
-#include "build.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "hostname-util.h"
+#include "locale-util.h"
#include "mkdir.h"
-#include "time-util.h"
+#include "parse-util.h"
#include "path-util.h"
#include "random-util.h"
-#include "locale-util.h"
-#include "ask-password-api.h"
+#include "string-util.h"
+#include "strv.h"
#include "terminal-util.h"
-#include "hostname-util.h"
+#include "time-util.h"
+#include "umask-util.h"
+#include "user-util.h"
static char *arg_root = NULL;
static char *arg_locale = NULL; /* $LANG */
@@ -53,15 +58,6 @@ static bool arg_copy_locale = false;
static bool arg_copy_timezone = false;
static bool arg_copy_root_password = false;
-static void clear_string(char *x) {
-
- if (!x)
- return;
-
- /* A delicious drop of snake-oil! */
- memset(x, 'x', strlen(x));
-}
-
static bool press_any_key(void) {
char k = 0;
bool need_nl = true;
@@ -466,9 +462,9 @@ static int prompt_root_password(void) {
msg2 = strjoina(draw_special_char(DRAW_TRIANGULAR_BULLET), " Please enter new root password again: ");
for (;;) {
- _cleanup_free_ char *a = NULL, *b = NULL;
+ _cleanup_string_free_erase_ char *a = NULL, *b = NULL;
- r = ask_password_tty(msg1, 0, false, NULL, &a);
+ r = ask_password_tty(msg1, NULL, 0, 0, NULL, &a);
if (r < 0)
return log_error_errno(r, "Failed to query root password: %m");
@@ -477,21 +473,15 @@ static int prompt_root_password(void) {
break;
}
- r = ask_password_tty(msg2, 0, false, NULL, &b);
- if (r < 0) {
- log_error_errno(r, "Failed to query root password: %m");
- clear_string(a);
- return r;
- }
+ r = ask_password_tty(msg2, NULL, 0, 0, NULL, &b);
+ if (r < 0)
+ return log_error_errno(r, "Failed to query root password: %m");
if (!streq(a, b)) {
log_error("Entered passwords did not match, please try again.");
- clear_string(a);
- clear_string(b);
continue;
}
- clear_string(b);
arg_root_password = a;
a = NULL;
break;
@@ -512,7 +502,7 @@ static int write_root_shadow(const char *path, const struct spwd *p) {
errno = 0;
if (putspent(p, f) != 0)
- return errno ? -errno : -EIO;
+ return errno > 0 ? -errno : -EIO;
return fflush_and_check(f);
}
@@ -550,9 +540,9 @@ static int process_root_password(void) {
mkdir_parents(etc_shadow, 0755);
- lock = take_password_lock(arg_root);
+ lock = take_etc_passwd_lock(arg_root);
if (lock < 0)
- return lock;
+ return log_error_errno(lock, "Failed to take a lock: %m");
if (arg_copy_root_password && arg_root) {
struct spwd *p;
@@ -564,8 +554,7 @@ static int process_root_password(void) {
if (!errno)
errno = EIO;
- log_error_errno(errno, "Failed to find shadow entry for root: %m");
- return -errno;
+ return log_error_errno(errno, "Failed to find shadow entry for root: %m");
}
r = write_root_shadow(etc_shadow, p);
@@ -600,10 +589,9 @@ static int process_root_password(void) {
item.sp_pwdp = crypt(arg_root_password, salt);
if (!item.sp_pwdp) {
if (!errno)
- errno = -EINVAL;
+ errno = EINVAL;
- log_error_errno(errno, "Failed to encrypt password: %m");
- return -errno;
+ return log_error_errno(errno, "Failed to encrypt password: %m");
}
item.sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY);
@@ -704,21 +692,12 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case ARG_ROOT:
- free(arg_root);
- arg_root = path_make_absolute_cwd(optarg);
- if (!arg_root)
- return log_oom();
-
- path_kill_slashes(arg_root);
-
- if (path_equal(arg_root, "/"))
- arg_root = mfree(arg_root);
-
+ r = parse_path_argument_and_warn(optarg, true, &arg_root);
+ if (r < 0)
+ return r;
break;
case ARG_LOCALE:
@@ -886,7 +865,7 @@ finish:
free(arg_locale_messages);
free(arg_timezone);
free(arg_hostname);
- clear_string(arg_root_password);
+ string_erase(arg_root_password);
free(arg_root_password);
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
diff --git a/src/fsck/fsck.c b/src/fsck/fsck.c
index bd3051f30d..109c4a7ae0 100644
--- a/src/fsck/fsck.c
+++ b/src/fsck/fsck.c
@@ -20,28 +20,34 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
-#include <stdbool.h>
#include <errno.h>
-#include <unistd.h>
#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
#include <sys/file.h>
-#include <sys/stat.h>
#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
#include "sd-bus.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 "alloc-util.h"
#include "bus-common-errors.h"
+#include "bus-error.h"
+#include "bus-util.h"
#include "device-util.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "parse-util.h"
#include "path-util.h"
+#include "proc-cmdline.h"
+#include "process-util.h"
+#include "signal-util.h"
#include "socket-util.h"
+#include "special.h"
+#include "stdio-util.h"
+#include "util.h"
/* exit codes as defined in fsck(8) */
enum {
@@ -60,14 +66,14 @@ static bool arg_force = false;
static bool arg_show_progress = false;
static const char *arg_repair = "-a";
-static void start_target(const char *target) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+static void start_target(const char *target, const char *mode) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
assert(target);
- r = bus_open_system_systemd(&bus);
+ r = bus_connect_system_systemd(&bus);
if (r < 0) {
log_error_errno(r, "Failed to get D-Bus connection: %m");
return;
@@ -83,7 +89,7 @@ static void start_target(const char *target) {
"StartUnitReplace",
&error,
NULL,
- "sss", "basic.target", target, "replace");
+ "sss", "basic.target", target, mode);
/* Don't print a warning if we aren't called during startup */
if (r < 0 && !sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_JOB))
@@ -270,7 +276,7 @@ static int fsck_progress_socket(void) {
int main(int argc, char *argv[]) {
_cleanup_close_pair_ int progress_pipe[2] = { -1, -1 };
- _cleanup_device_unref_ sd_device *dev = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
const char *device, *type;
bool root_directory;
siginfo_t status;
@@ -366,12 +372,12 @@ int main(int argc, char *argv[]) {
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);
- r = 0;
+ if (r < 0)
+ log_warning_errno(r, "Couldn't detect if fsck.%s may be used for %s, proceeding: %m", type, device);
+ else if (r == 0) {
+ log_info("fsck.%s doesn't exist, not checking file system on %s.", type, device);
goto finish;
- } else if (r < 0)
- log_warning_errno(r, "Couldn't detect if fsck.%s may be used for %s: %m", type, device);
+ }
}
if (arg_show_progress) {
@@ -463,10 +469,10 @@ int main(int argc, char *argv[]) {
if (status.si_code == CLD_EXITED && (status.si_status & FSCK_SYSTEM_SHOULD_REBOOT) && root_directory)
/* System should be rebooted. */
- start_target(SPECIAL_REBOOT_TARGET);
+ start_target(SPECIAL_REBOOT_TARGET, "replace-irreversibly");
else if (status.si_code == CLD_EXITED && (status.si_status & (FSCK_SYSTEM_SHOULD_REBOOT | FSCK_ERRORS_LEFT_UNCORRECTED)))
/* Some other problem */
- start_target(SPECIAL_EMERGENCY_TARGET);
+ start_target(SPECIAL_EMERGENCY_TARGET, "replace");
else {
log_warning("Ignoring error.");
r = 0;
diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c
index 3f8ea5647c..1468dc8df6 100644
--- a/src/fstab-generator/fstab-generator.c
+++ b/src/fstab-generator/fstab-generator.c
@@ -19,22 +19,30 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
-#include <mntent.h>
#include <errno.h>
+#include <mntent.h>
+#include <stdio.h>
#include <string.h>
#include <unistd.h>
-#include "log.h"
-#include "util.h"
-#include "unit-name.h"
-#include "path-util.h"
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fileio.h"
#include "fstab-util.h"
+#include "generator.h"
+#include "log.h"
+#include "mkdir.h"
#include "mount-setup.h"
+#include "mount-util.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "proc-cmdline.h"
#include "special.h"
-#include "mkdir.h"
-#include "generator.h"
+#include "stat-util.h"
+#include "string-util.h"
#include "strv.h"
+#include "unit-name.h"
+#include "util.h"
#include "virt.h"
static const char *arg_dest = "/tmp";
@@ -240,6 +248,7 @@ static int add_mount(
assert(what);
assert(where);
assert(opts);
+ assert(post);
assert(source);
if (streq_ptr(fstype, "autofs"))
@@ -289,7 +298,7 @@ static int add_mount(
"Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
source);
- if (post && !noauto && !nofail && !automount)
+ if (!noauto && !nofail && !automount)
fprintf(f, "Before=%s\n", post);
if (!automount && opts) {
@@ -329,7 +338,7 @@ static int add_mount(
if (r < 0)
return log_error_errno(r, "Failed to write unit file %s: %m", unit);
- if (!noauto && post) {
+ if (!noauto) {
lnk = strjoin(arg_dest, "/", post, nofail || automount ? ".wants/" : ".requires/", name, NULL);
if (!lnk)
return log_oom();
@@ -360,10 +369,7 @@ static int add_mount(
"Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
source);
- if (post)
- fprintf(f,
- "Before=%s\n",
- post);
+ fprintf(f, "Before=%s\n", post);
if (opts) {
r = write_requires_after(f, opts);
@@ -457,8 +463,6 @@ static int parse_fstab(bool initrd) {
"x-systemd.automount\0");
if (initrd)
post = SPECIAL_INITRD_FS_TARGET;
- else if (mount_in_initrd(me))
- post = SPECIAL_INITRD_ROOT_FS_TARGET;
else if (mount_is_network(me))
post = SPECIAL_REMOTE_FS_TARGET;
else
@@ -570,7 +574,7 @@ static int add_sysroot_usr_mount(void) {
false,
false,
false,
- SPECIAL_INITRD_ROOT_FS_TARGET,
+ SPECIAL_INITRD_FS_TARGET,
"/proc/cmdline");
}
diff --git a/src/getty-generator/getty-generator.c b/src/getty-generator/getty-generator.c
index 9a4b038ef3..bddc0c441a 100644
--- a/src/getty-generator/getty-generator.c
+++ b/src/getty-generator/getty-generator.c
@@ -19,20 +19,23 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <string.h>
#include <errno.h>
-#include <unistd.h>
#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fileio.h"
#include "log.h"
-#include "util.h"
#include "mkdir.h"
-#include "unit-name.h"
-#include "virt.h"
-#include "fileio.h"
#include "path-util.h"
#include "process-util.h"
+#include "string-util.h"
#include "terminal-util.h"
+#include "unit-name.h"
+#include "util.h"
+#include "virt.h"
static const char *arg_dest = "/tmp";
@@ -109,7 +112,7 @@ static int verify_tty(const char *name) {
errno = 0;
if (isatty(fd) <= 0)
- return errno ? -errno : -EIO;
+ return errno > 0 ? -errno : -EIO;
return 0;
}
diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c
index bb821797f1..ce8cecc5cb 100644
--- a/src/gpt-auto-generator/gpt-auto-generator.c
+++ b/src/gpt-auto-generator/gpt-auto-generator.c
@@ -19,27 +19,37 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
+#include <blkid/blkid.h>
#include <stdlib.h>
#include <sys/statfs.h>
-#include <blkid/blkid.h>
+#include <unistd.h>
-#include "sd-id128.h"
#include "libudev.h"
-#include "path-util.h"
-#include "util.h"
-#include "mkdir.h"
+#include "sd-id128.h"
+
+#include "alloc-util.h"
+#include "blkid-util.h"
+#include "btrfs-util.h"
+#include "dirent-util.h"
+#include "efivars.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fstab-util.h"
+#include "generator.h"
+#include "gpt.h"
#include "missing.h"
-#include "udev-util.h"
+#include "mkdir.h"
+#include "mount-util.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "proc-cmdline.h"
#include "special.h"
+#include "stat-util.h"
+#include "string-util.h"
+#include "udev-util.h"
#include "unit-name.h"
+#include "util.h"
#include "virt.h"
-#include "generator.h"
-#include "gpt.h"
-#include "fileio.h"
-#include "efivars.h"
-#include "blkid-util.h"
-#include "btrfs-util.h"
static const char *arg_dest = "/tmp";
static bool arg_enabled = true;
@@ -292,8 +302,7 @@ static int probe_and_add_mount(
if (!b) {
if (errno == 0)
return log_oom();
- log_error_errno(errno, "Failed to allocate prober: %m");
- return -errno;
+ return log_error_errno(errno, "Failed to allocate prober: %m");
}
blkid_probe_enable_superblocks(b, 1);
@@ -465,6 +474,12 @@ static int add_boot(const char *what) {
return 0;
}
+ /* We create an .automount which is not overridden by the .mount from the fstab generator. */
+ if (fstab_is_mount_point("/boot")) {
+ log_debug("/boot specified in fstab, ignoring.");
+ return 0;
+ }
+
if (path_is_busy("/boot")) {
log_debug("/boot already populated, ignoring.");
return 0;
@@ -486,8 +501,7 @@ static int add_boot(const char *what) {
if (!b) {
if (errno == 0)
return log_oom();
- log_error_errno(errno, "Failed to allocate prober: %m");
- return -errno;
+ return log_error_errno(errno, "Failed to allocate prober: %m");
}
blkid_probe_enable_partitions(b, 1);
@@ -864,7 +878,6 @@ static int get_block_device_harder(const char *path, dev_t *dev) {
goto fallback;
found = de;
- break;
}
if (!found)
diff --git a/src/hibernate-resume/hibernate-resume-generator.c b/src/hibernate-resume/hibernate-resume-generator.c
index 9fb6233336..da719f2a30 100644
--- a/src/hibernate-resume/hibernate-resume-generator.c
+++ b/src/hibernate-resume/hibernate-resume-generator.c
@@ -19,14 +19,18 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
#include <errno.h>
+#include <stdio.h>
+#include "alloc-util.h"
+#include "fstab-util.h"
#include "log.h"
-#include "util.h"
-#include "special.h"
#include "mkdir.h"
+#include "proc-cmdline.h"
+#include "special.h"
+#include "string-util.h"
#include "unit-name.h"
+#include "util.h"
static const char *arg_dest = "/tmp";
static char *arg_resume_dev = NULL;
diff --git a/src/hibernate-resume/hibernate-resume.c b/src/hibernate-resume/hibernate-resume.c
index 1f3b169905..2e1259ef68 100644
--- a/src/hibernate-resume/hibernate-resume.c
+++ b/src/hibernate-resume/hibernate-resume.c
@@ -19,13 +19,14 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
#include <errno.h>
+#include <stdio.h>
#include <sys/stat.h>
+#include "alloc-util.h"
+#include "fileio.h"
#include "log.h"
#include "util.h"
-#include "fileio.h"
int main(int argc, char *argv[]) {
struct stat st;
diff --git a/src/hostname/hostnamectl.c b/src/hostname/hostnamectl.c
index dcbad99ae9..940f6f7e23 100644
--- a/src/hostname/hostnamectl.c
+++ b/src/hostname/hostnamectl.c
@@ -19,21 +19,22 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
-#include <stdbool.h>
#include <getopt.h>
#include <locale.h>
+#include <stdbool.h>
+#include <stdlib.h>
#include <string.h>
#include "sd-bus.h"
#include "sd-id128.h"
-#include "hostname-util.h"
-#include "bus-util.h"
+
+#include "alloc-util.h"
+#include "architecture.h"
#include "bus-error.h"
-#include "util.h"
+#include "bus-util.h"
+#include "hostname-util.h"
#include "spawn-polkit-agent.h"
-#include "build.h"
-#include "architecture.h"
+#include "util.h"
static bool arg_ask_password = true;
static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
@@ -126,8 +127,8 @@ static void print_status_info(StatusInfo *i) {
}
static int show_one_name(sd_bus *bus, const char* attr) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
const char *s;
int r;
@@ -232,7 +233,7 @@ static int show_status(sd_bus *bus, char **args, unsigned n) {
}
static int set_simple_string(sd_bus *bus, const char *method, const char *value) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r = 0;
polkit_agent_open_if_enabled();
@@ -387,9 +388,7 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case 'H':
arg_transport = BUS_TRANSPORT_REMOTE;
@@ -508,7 +507,7 @@ static int hostnamectl_main(sd_bus *bus, int argc, char *argv[]) {
}
int main(int argc, char *argv[]) {
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
setlocale(LC_ALL, "");
@@ -519,7 +518,7 @@ int main(int argc, char *argv[]) {
if (r <= 0)
goto finish;
- r = bus_open_transport(arg_transport, arg_host, false, &bus);
+ r = bus_connect_transport(arg_transport, arg_host, false, &bus);
if (r < 0) {
log_error_errno(r, "Failed to create bus connection: %m");
goto finish;
diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c
index 96cc8951e6..84605fa267 100644
--- a/src/hostname/hostnamed.c
+++ b/src/hostname/hostnamed.c
@@ -21,19 +21,22 @@
#include <errno.h>
#include <string.h>
-#include <unistd.h>
#include <sys/utsname.h>
+#include <unistd.h>
-#include "util.h"
-#include "strv.h"
+#include "alloc-util.h"
+#include "bus-util.h"
#include "def.h"
-#include "virt.h"
#include "env-util.h"
#include "fileio-label.h"
-#include "bus-util.h"
-#include "event-util.h"
-#include "selinux-util.h"
#include "hostname-util.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "selinux-util.h"
+#include "strv.h"
+#include "user-util.h"
+#include "util.h"
+#include "virt.h"
#define VALID_DEPLOYMENT_CHARS (DIGITS LETTERS "-.:")
@@ -209,10 +212,10 @@ try_dmi:
unreliable enough, so let's not do any additional guesswork
on top of that.
- See the SMBIOS Specification 2.7.1 section 7.4.1 for
+ See the SMBIOS Specification 3.0 section 7.4.1 for
details about the values listed here:
- http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.1.pdf
+ https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.0.0.pdf
*/
switch (t) {
@@ -234,7 +237,11 @@ try_dmi:
case 0x11:
case 0x1C:
+ case 0x1D:
return "server";
+
+ case 0x1E:
+ return "tablet";
}
return NULL;
@@ -661,7 +668,7 @@ static const sd_bus_vtable hostname_vtable[] = {
};
static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
assert(c);
@@ -692,8 +699,8 @@ static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
int main(int argc, char *argv[]) {
Context context = {};
- _cleanup_event_unref_ sd_event *event = NULL;
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
log_set_target(LOG_TARGET_AUTO);
@@ -709,12 +716,6 @@ int main(int argc, char *argv[]) {
goto finish;
}
- if (argc != 1) {
- log_error("This program takes no arguments.");
- r = -EINVAL;
- goto finish;
- }
-
r = sd_event_default(&event);
if (r < 0) {
log_error_errno(r, "Failed to allocate event loop: %m");
diff --git a/src/hwdb/hwdb.c b/src/hwdb/hwdb.c
index 446de3a2fc..1160dacdf1 100644
--- a/src/hwdb/hwdb.c
+++ b/src/hwdb/hwdb.c
@@ -17,21 +17,24 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
+#include <ctype.h>
#include <getopt.h>
+#include <stdlib.h>
#include <string.h>
-#include <ctype.h>
-#include "util.h"
-#include "strbuf.h"
+#include "alloc-util.h"
#include "conf-files.h"
-#include "strv.h"
-#include "mkdir.h"
-#include "verbs.h"
-#include "build.h"
-
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
#include "hwdb-internal.h"
#include "hwdb-util.h"
+#include "mkdir.h"
+#include "strbuf.h"
+#include "string-util.h"
+#include "strv.h"
+#include "util.h"
+#include "verbs.h"
/*
* Generic udev properties, key/value database based on modalias strings.
@@ -568,7 +571,7 @@ static int import_file(struct trie *trie, const char *filename) {
}
static int hwdb_query(int argc, char *argv[], void *userdata) {
- _cleanup_hwdb_unref_ sd_hwdb *hwdb = NULL;
+ _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
const char *key, *value;
const char *modalias;
int r;
@@ -688,9 +691,7 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case ARG_USR:
arg_hwdb_bin_dir = UDEVLIBEXECDIR;
diff --git a/src/import/aufs-util.c b/src/import/aufs-util.c
index 18c42b8b6d..b44dbb14ea 100644
--- a/src/import/aufs-util.c
+++ b/src/import/aufs-util.c
@@ -21,9 +21,10 @@
#include <ftw.h>
-#include "util.h"
-#include "rm-rf.h"
#include "aufs-util.h"
+#include "rm-rf.h"
+#include "string-util.h"
+#include "util.h"
static int nftw_cb(
const char *fpath,
@@ -68,7 +69,7 @@ int aufs_resolve(const char *path) {
errno = 0;
r = nftw(path, nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
if (r == FTW_STOP)
- return errno ? -errno : -EIO;
+ return errno > 0 ? -errno : -EIO;
return 0;
}
diff --git a/src/import/curl-util.c b/src/import/curl-util.c
index d390cfb1f3..8e531a64fa 100644
--- a/src/import/curl-util.c
+++ b/src/import/curl-util.c
@@ -19,7 +19,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "alloc-util.h"
#include "curl-util.h"
+#include "fd-util.h"
+#include "string-util.h"
static void curl_glue_check_finished(CurlGlue *g) {
CURLMsg *msg;
@@ -45,9 +48,7 @@ static int curl_glue_on_io(sd_event_source *s, int fd, uint32_t revents, void *u
assert(s);
assert(g);
- translated_fd = PTR_TO_INT(hashmap_get(g->translate_fds, INT_TO_PTR(fd+1)));
- assert(translated_fd > 0);
- translated_fd--;
+ translated_fd = PTR_TO_FD(hashmap_get(g->translate_fds, FD_TO_PTR(fd)));
if ((revents & (EPOLLIN|EPOLLOUT)) == (EPOLLIN|EPOLLOUT))
action = CURL_POLL_INOUT;
@@ -76,7 +77,7 @@ static int curl_glue_socket_callback(CURLM *curl, curl_socket_t s, int action, v
assert(curl);
assert(g);
- io = hashmap_get(g->ios, INT_TO_PTR(s+1));
+ io = hashmap_get(g->ios, FD_TO_PTR(s));
if (action == CURL_POLL_REMOVE) {
if (io) {
@@ -88,8 +89,8 @@ static int curl_glue_socket_callback(CURLM *curl, curl_socket_t s, int action, v
sd_event_source_set_enabled(io, SD_EVENT_OFF);
sd_event_source_unref(io);
- hashmap_remove(g->ios, INT_TO_PTR(s+1));
- hashmap_remove(g->translate_fds, INT_TO_PTR(fd+1));
+ hashmap_remove(g->ios, FD_TO_PTR(s));
+ hashmap_remove(g->translate_fds, FD_TO_PTR(fd));
safe_close(fd);
}
@@ -140,17 +141,17 @@ static int curl_glue_socket_callback(CURLM *curl, curl_socket_t s, int action, v
sd_event_source_set_description(io, "curl-io");
- r = hashmap_put(g->ios, INT_TO_PTR(s+1), io);
+ r = hashmap_put(g->ios, FD_TO_PTR(s), io);
if (r < 0) {
log_oom();
sd_event_source_unref(io);
return -1;
}
- r = hashmap_put(g->translate_fds, INT_TO_PTR(fd+1), INT_TO_PTR(s+1));
+ r = hashmap_put(g->translate_fds, FD_TO_PTR(fd), FD_TO_PTR(s));
if (r < 0) {
log_oom();
- hashmap_remove(g->ios, INT_TO_PTR(s+1));
+ hashmap_remove(g->ios, FD_TO_PTR(s));
sd_event_source_unref(io);
return -1;
}
@@ -226,7 +227,7 @@ CurlGlue *curl_glue_unref(CurlGlue *g) {
fd = sd_event_source_get_io_fd(io);
assert(fd >= 0);
- hashmap_remove(g->translate_fds, INT_TO_PTR(fd+1));
+ hashmap_remove(g->translate_fds, FD_TO_PTR(fd));
safe_close(fd);
sd_event_source_unref(io);
diff --git a/src/import/curl-util.h b/src/import/curl-util.h
index c249069ffa..eec53c9266 100644
--- a/src/import/curl-util.h
+++ b/src/import/curl-util.h
@@ -21,12 +21,13 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
#include <curl/curl.h>
+#include <sys/types.h>
-#include "hashmap.h"
#include "sd-event.h"
+#include "hashmap.h"
+
typedef struct CurlGlue CurlGlue;
struct CurlGlue {
diff --git a/src/import/export-raw.c b/src/import/export-raw.c
index 8f9c9bbc80..28c87594d6 100644
--- a/src/import/export-raw.c
+++ b/src/import/export-raw.c
@@ -20,16 +20,25 @@
***/
#include <sys/sendfile.h>
+
+/* When we include libgen.h because we need dirname() we immediately
+ * undefine basename() since libgen.h defines it as a macro to the POSIX
+ * version which is really broken. We prefer GNU basename(). */
#include <libgen.h>
#undef basename
#include "sd-daemon.h"
-#include "util.h"
-#include "ratelimit.h"
+
+#include "alloc-util.h"
#include "btrfs-util.h"
#include "copy.h"
-#include "import-common.h"
#include "export-raw.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "import-common.h"
+#include "ratelimit.h"
+#include "string-util.h"
+#include "util.h"
#define COPY_BUFFER_SIZE (16*1024)
diff --git a/src/import/export-raw.h b/src/import/export-raw.h
index b71de6cb82..e5e298f6ab 100644
--- a/src/import/export-raw.h
+++ b/src/import/export-raw.h
@@ -22,8 +22,9 @@
***/
#include "sd-event.h"
-#include "macro.h"
+
#include "import-compress.h"
+#include "macro.h"
typedef struct RawExport RawExport;
diff --git a/src/import/export-tar.c b/src/import/export-tar.c
index 43fa9d1b03..2bbec661e6 100644
--- a/src/import/export-tar.c
+++ b/src/import/export-tar.c
@@ -19,15 +19,18 @@
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 "alloc-util.h"
#include "btrfs-util.h"
-#include "import-common.h"
#include "export-tar.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "import-common.h"
#include "process-util.h"
+#include "ratelimit.h"
+#include "string-util.h"
+#include "util.h"
#define COPY_BUFFER_SIZE (16*1024)
@@ -78,7 +81,7 @@ TarExport *tar_export_unref(TarExport *e) {
}
if (e->temp_path) {
- (void) btrfs_subvol_remove(e->temp_path, false);
+ (void) btrfs_subvol_remove(e->temp_path, BTRFS_REMOVE_QUOTA);
free(e->temp_path);
}
@@ -283,7 +286,7 @@ int tar_export_start(TarExport *e, const char *path, int fd, ImportCompressType
if (e->st.st_ino == 256) { /* might be a btrfs subvolume? */
BtrfsQuotaInfo q;
- r = btrfs_subvol_get_quota_fd(sfd, &q);
+ r = btrfs_subvol_get_subtree_quota_fd(sfd, 0, &q);
if (r >= 0)
e->quota_referenced = q.referenced;
diff --git a/src/import/export-tar.h b/src/import/export-tar.h
index ce27a9fc1e..9061e7515d 100644
--- a/src/import/export-tar.h
+++ b/src/import/export-tar.h
@@ -22,8 +22,9 @@
***/
#include "sd-event.h"
-#include "macro.h"
+
#include "import-compress.h"
+#include "macro.h"
typedef struct TarExport TarExport;
diff --git a/src/import/export.c b/src/import/export.c
index b88d71fec6..27efc3b099 100644
--- a/src/import/export.c
+++ b/src/import/export.c
@@ -22,15 +22,18 @@
#include <getopt.h>
#include "sd-event.h"
-#include "event-util.h"
-#include "signal-util.h"
+
+#include "alloc-util.h"
+#include "export-raw.h"
+#include "export-tar.h"
+#include "fd-util.h"
+#include "fs-util.h"
#include "hostname-util.h"
-#include "verbs.h"
-#include "build.h"
-#include "machine-image.h"
#include "import-util.h"
-#include "export-tar.h"
-#include "export-raw.h"
+#include "machine-image.h"
+#include "signal-util.h"
+#include "string-util.h"
+#include "verbs.h"
static ImportCompressType arg_compress = IMPORT_COMPRESS_UNKNOWN;
@@ -72,7 +75,7 @@ static void on_tar_finished(TarExport *export, int error, void *userdata) {
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_(sd_event_unrefp) sd_event *event = NULL;
_cleanup_(image_unrefp) Image *image = NULL;
const char *path = NULL, *local = NULL;
_cleanup_close_ int open_fd = -1;
@@ -151,7 +154,7 @@ static void on_raw_finished(RawExport *export, int error, void *userdata) {
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_(sd_event_unrefp) sd_event *event = NULL;
_cleanup_(image_unrefp) Image *image = NULL;
const char *path = NULL, *local = NULL;
_cleanup_close_ int open_fd = -1;
@@ -260,9 +263,7 @@ static int parse_argv(int argc, char *argv[]) {
return help(0, NULL, NULL);
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case ARG_FORMAT:
if (streq(optarg, "uncompressed"))
diff --git a/src/import/import-common.c b/src/import/import-common.c
index d8a3bbc249..8a48bd7bf9 100644
--- a/src/import/import-common.c
+++ b/src/import/import-common.c
@@ -19,15 +19,17 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <sched.h>
#include <sys/prctl.h>
#include <sys/stat.h>
#include <unistd.h>
-#include "util.h"
#include "btrfs-util.h"
-#include "capability.h"
-#include "signal-util.h"
+#include "capability-util.h"
+#include "fd-util.h"
#include "import-common.h"
+#include "signal-util.h"
+#include "util.h"
int import_make_read_only_fd(int fd) {
int r;
@@ -132,7 +134,7 @@ int import_fork_tar_x(const char *path, pid_t *ret) {
if (unshare(CLONE_NEWNET) < 0)
log_error_errno(errno, "Failed to lock tar into network namespace, ignoring: %m");
- r = capability_bounding_set_drop(~retain, true);
+ r = capability_bounding_set_drop(retain, true);
if (r < 0)
log_error_errno(r, "Failed to drop capabilities, ignoring: %m");
@@ -206,7 +208,7 @@ int import_fork_tar_c(const char *path, pid_t *ret) {
if (unshare(CLONE_NEWNET) < 0)
log_error_errno(errno, "Failed to lock tar into network namespace, ignoring: %m");
- r = capability_bounding_set_drop(~retain, true);
+ r = capability_bounding_set_drop(retain, true);
if (r < 0)
log_error_errno(r, "Failed to drop capabilities, ignoring: %m");
diff --git a/src/import/import-compress.c b/src/import/import-compress.c
index d6b8133036..d4ff178f60 100644
--- a/src/import/import-compress.c
+++ b/src/import/import-compress.c
@@ -19,8 +19,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
#include "import-compress.h"
+#include "string-table.h"
+#include "util.h"
void import_compress_free(ImportCompress *c) {
assert(c);
diff --git a/src/import/import-compress.h b/src/import/import-compress.h
index 50d91f732c..0a13232554 100644
--- a/src/import/import-compress.h
+++ b/src/import/import-compress.h
@@ -21,11 +21,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
-
+#include <bzlib.h>
#include <lzma.h>
+#include <sys/types.h>
#include <zlib.h>
-#include <bzlib.h>
#include "macro.h"
diff --git a/src/import/import-raw.c b/src/import/import-raw.c
index 5f7d25d063..7593f064fc 100644
--- a/src/import/import-raw.c
+++ b/src/import/import-raw.c
@@ -23,19 +23,27 @@
#include "sd-daemon.h"
#include "sd-event.h"
-#include "util.h"
-#include "path-util.h"
+
+#include "alloc-util.h"
#include "btrfs-util.h"
-#include "hostname-util.h"
+#include "chattr-util.h"
#include "copy.h"
-#include "mkdir.h"
-#include "rm-rf.h"
-#include "ratelimit.h"
-#include "machine-pool.h"
-#include "qcow2-util.h"
-#include "import-compress.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "hostname-util.h"
#include "import-common.h"
+#include "import-compress.h"
#include "import-raw.h"
+#include "io-util.h"
+#include "machine-pool.h"
+#include "mkdir.h"
+#include "path-util.h"
+#include "qcow2-util.h"
+#include "ratelimit.h"
+#include "rm-rf.h"
+#include "string-util.h"
+#include "util.h"
struct RawImport {
sd_event *event;
@@ -191,7 +199,7 @@ static int raw_import_maybe_convert_qcow2(RawImport *i) {
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_warning_errno(r, "Failed to set file attributes on %s: %m", t);
log_info("Unpacking QCOW2 file.");
@@ -279,7 +287,7 @@ static int raw_import_open_disk(RawImport *i) {
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", i->temp_path);
+ log_warning_errno(r, "Failed to set file attributes on %s: %m", i->temp_path);
return 0;
}
diff --git a/src/import/import-raw.h b/src/import/import-raw.h
index bf7c770340..626d965cf8 100644
--- a/src/import/import-raw.h
+++ b/src/import/import-raw.h
@@ -22,8 +22,9 @@
***/
#include "sd-event.h"
-#include "macro.h"
+
#include "import-util.h"
+#include "macro.h"
typedef struct RawImport RawImport;
diff --git a/src/import/import-tar.c b/src/import/import-tar.c
index d2bfb30238..c7983c04be 100644
--- a/src/import/import-tar.c
+++ b/src/import/import-tar.c
@@ -23,20 +23,27 @@
#include "sd-daemon.h"
#include "sd-event.h"
-#include "util.h"
-#include "path-util.h"
+
+#include "alloc-util.h"
#include "btrfs-util.h"
-#include "hostname-util.h"
#include "copy.h"
-#include "mkdir.h"
-#include "rm-rf.h"
-#include "ratelimit.h"
-#include "machine-pool.h"
-#include "qcow2-util.h"
-#include "import-compress.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "hostname-util.h"
#include "import-common.h"
+#include "import-compress.h"
#include "import-tar.h"
+#include "io-util.h"
+#include "machine-pool.h"
+#include "mkdir.h"
+#include "path-util.h"
#include "process-util.h"
+#include "qcow2-util.h"
+#include "ratelimit.h"
+#include "rm-rf.h"
+#include "string-util.h"
+#include "util.h"
struct TarImport {
sd_event *event;
@@ -234,7 +241,9 @@ static int tar_import_fork_tar(TarImport *i) {
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);
+ return log_error_errno(r, "Failed to create subvolume %s: %m", i->temp_path);
+ else
+ (void) import_assign_pool_quota_and_warn(i->temp_path);
i->tar_fd = import_fork_tar_x(i->temp_path, &i->tar_pid);
if (i->tar_fd < 0)
diff --git a/src/import/import-tar.h b/src/import/import-tar.h
index aaecb51398..d12391572d 100644
--- a/src/import/import-tar.h
+++ b/src/import/import-tar.h
@@ -22,8 +22,9 @@
***/
#include "sd-event.h"
-#include "macro.h"
+
#include "import-util.h"
+#include "macro.h"
typedef struct TarImport TarImport;
diff --git a/src/import/import.c b/src/import/import.c
index 929a840298..15505de924 100644
--- a/src/import/import.c
+++ b/src/import/import.c
@@ -22,15 +22,18 @@
#include <getopt.h>
#include "sd-event.h"
-#include "event-util.h"
-#include "verbs.h"
-#include "build.h"
-#include "signal-util.h"
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fs-util.h"
#include "hostname-util.h"
-#include "machine-image.h"
-#include "import-util.h"
-#include "import-tar.h"
#include "import-raw.h"
+#include "import-tar.h"
+#include "import-util.h"
+#include "machine-image.h"
+#include "signal-util.h"
+#include "string-util.h"
+#include "verbs.h"
static bool arg_force = false;
static bool arg_read_only = false;
@@ -54,7 +57,7 @@ static void on_tar_finished(TarImport *import, int error, void *userdata) {
static int import_tar(int argc, char *argv[], void *userdata) {
_cleanup_(tar_import_unrefp) TarImport *import = NULL;
- _cleanup_event_unref_ sd_event *event = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
const char *path = NULL, *local = NULL;
_cleanup_free_ char *ll = NULL;
_cleanup_close_ int open_fd = -1;
@@ -149,7 +152,7 @@ static void on_raw_finished(RawImport *import, int error, void *userdata) {
static int import_raw(int argc, char *argv[], void *userdata) {
_cleanup_(raw_import_unrefp) RawImport *import = NULL;
- _cleanup_event_unref_ sd_event *event = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
const char *path = NULL, *local = NULL;
_cleanup_free_ char *ll = NULL;
_cleanup_close_ int open_fd = -1;
@@ -280,9 +283,7 @@ static int parse_argv(int argc, char *argv[]) {
return help(0, NULL, NULL);
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case ARG_FORCE:
arg_force = true;
diff --git a/src/import/importd.c b/src/import/importd.c
index c90ada5da4..1b777c32b6 100644
--- a/src/import/importd.c
+++ b/src/import/importd.c
@@ -22,20 +22,28 @@
#include <sys/prctl.h>
#include "sd-bus.h"
-#include "util.h"
-#include "strv.h"
-#include "bus-util.h"
+
+#include "alloc-util.h"
#include "bus-common-errors.h"
-#include "socket-util.h"
-#include "mkdir.h"
+#include "bus-util.h"
#include "def.h"
-#include "missing.h"
+#include "fd-util.h"
+#include "hostname-util.h"
+#include "import-util.h"
#include "machine-pool.h"
+#include "missing.h"
+#include "mkdir.h"
+#include "parse-util.h"
#include "path-util.h"
-#include "import-util.h"
#include "process-util.h"
#include "signal-util.h"
-#include "hostname-util.h"
+#include "socket-util.h"
+#include "string-table.h"
+#include "strv.h"
+#include "syslog-util.h"
+#include "user-util.h"
+#include "util.h"
+#include "web-util.h"
typedef struct Transfer Transfer;
typedef struct Manager Manager;
@@ -47,7 +55,6 @@ typedef enum TransferType {
TRANSFER_EXPORT_RAW,
TRANSFER_PULL_TAR,
TRANSFER_PULL_RAW,
- TRANSFER_PULL_DKR,
_TRANSFER_TYPE_MAX,
_TRANSFER_TYPE_INVALID = -1,
} TransferType;
@@ -66,7 +73,6 @@ struct Transfer {
bool force_local;
bool read_only;
- char *dkr_index_url;
char *format;
pid_t pid;
@@ -109,7 +115,6 @@ static const char* const transfer_type_table[_TRANSFER_TYPE_MAX] = {
[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);
@@ -126,7 +131,6 @@ static Transfer *transfer_unref(Transfer *t) {
free(t->remote);
free(t->local);
- free(t->dkr_index_url);
free(t->format);
free(t->object_path);
@@ -375,12 +379,11 @@ static int transfer_start(Transfer *t) {
if (t->pid == 0) {
const char *cmd[] = {
NULL, /* systemd-import, systemd-export or systemd-pull */
- NULL, /* tar, raw, dkr */
+ NULL, /* tar, raw */
NULL, /* --verify= */
NULL, /* verify argument */
NULL, /* maybe --force */
NULL, /* maybe --read-only */
- NULL, /* maybe --dkr-index-url */
NULL, /* if so: the actual URL */
NULL, /* maybe --format= */
NULL, /* if so: the actual format */
@@ -463,10 +466,8 @@ static int transfer_start(Transfer *t) {
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";
+ cmd[k++] = "raw";
if (t->verify != _IMPORT_VERIFY_INVALID) {
cmd[k++] = "--verify";
@@ -478,11 +479,6 @@ static int transfer_start(Transfer *t) {
if (t->read_only)
cmd[k++] = "--read-only";
- if (t->dkr_index_url) {
- cmd[k++] = "--dkr-index-url";
- cmd[k++] = t->dkr_index_url;
- }
-
if (t->format) {
cmd[k++] = "--format";
cmd[k++] = t->format;
@@ -600,11 +596,11 @@ static int manager_on_notify(sd_event_source *s, int fd, uint32_t revents, void
cmsg_close_all(&msghdr);
- CMSG_FOREACH(cmsg, &msghdr) {
- if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS && cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)))
-
+ CMSG_FOREACH(cmsg, &msghdr)
+ 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);
- }
if (msghdr.msg_flags & MSG_TRUNC) {
log_warning("Got overly long notification datagram, ignoring.");
@@ -699,7 +695,7 @@ static int manager_new(Manager **ret) {
return 0;
}
-static Transfer *manager_find(Manager *m, TransferType type, const char *dkr_index_url, const char *remote) {
+static Transfer *manager_find(Manager *m, TransferType type, const char *remote) {
Transfer *t;
Iterator i;
@@ -710,8 +706,7 @@ static Transfer *manager_find(Manager *m, TransferType type, const char *dkr_ind
HASHMAP_FOREACH(t, m->transfers, i) {
if (t->type == type &&
- streq_ptr(t->remote, remote) &&
- streq_ptr(t->dkr_index_url, dkr_index_url))
+ streq_ptr(t->remote, remote))
return t;
}
@@ -899,7 +894,7 @@ static int method_pull_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_er
type = streq_ptr(sd_bus_message_get_member(msg), "PullTar") ? TRANSFER_PULL_TAR : TRANSFER_PULL_RAW;
- if (manager_find(m, type, NULL, remote))
+ if (manager_find(m, type, remote))
return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, "Transfer for %s already in progress.", remote);
r = transfer_new(m, &t);
@@ -931,107 +926,8 @@ static int method_pull_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_er
return sd_bus_reply_method_return(msg, "uo", id, object);
}
-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;
- ImportVerify v;
- int force, r;
- uint32_t id;
-
- assert(msg);
- assert(m);
-
- r = bus_verify_polkit_async(
- msg,
- CAP_SYS_ADMIN,
- "org.freedesktop.import1.pull",
- NULL,
- 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, "sssssb", &index_url, &remote, &tag, &local, &verify, &force);
- if (r < 0)
- return r;
-
- if (isempty(index_url))
- index_url = DEFAULT_DKR_INDEX_URL;
- if (!index_url)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Index URL must be specified.");
- if (!http_url_is_valid(index_url))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Index URL %s is invalid", index_url);
-
- if (!dkr_name_is_valid(remote))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Remote name %s is not valid", remote);
-
- if (isempty(tag))
- tag = "latest";
- else if (!dkr_tag_is_valid(tag))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Tag %s is not valid", tag);
-
- if (isempty(local))
- local = NULL;
- else if (!machine_name_is_valid(local))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
-
- if (isempty(verify))
- v = IMPORT_VERIFY_SIGNATURE;
- else
- v = import_verify_from_string(verify);
- if (v < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify);
-
- if (v != IMPORT_VERIFY_NO)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "DKR does not support verification.");
-
- 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_PULL_DKR;
- t->verify = v;
- t->force_local = force;
-
- t->dkr_index_url = strdup(index_url);
- if (!t->dkr_index_url)
- return -ENOMEM;
-
- t->remote = strjoin(remote, ":", tag, NULL);
- if (!t->remote)
- return -ENOMEM;
-
- if (local) {
- t->local = strdup(local);
- if (!t->local)
- return -ENOMEM;
- }
-
- 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_list_transfers(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
Manager *m = userdata;
Transfer *t;
Iterator i;
@@ -1180,7 +1076,6 @@ static const sd_bus_vtable manager_vtable[] = {
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),
SD_BUS_METHOD("ListTransfers", NULL, "a(usssdo)", method_list_transfers, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CancelTransfer", "u", NULL, method_cancel_transfer, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_SIGNAL("TransferNew", "uo", 0),
diff --git a/src/import/org.freedesktop.import1.conf b/src/import/org.freedesktop.import1.conf
index ae36af422f..ed2539a03b 100644
--- a/src/import/org.freedesktop.import1.conf
+++ b/src/import/org.freedesktop.import1.conf
@@ -53,10 +53,6 @@
send_member="PullRaw"/>
<allow send_destination="org.freedesktop.import1"
- send_interface="org.freedesktop.import1.Manager"
- send_member="PullDkr"/>
-
- <allow send_destination="org.freedesktop.import1"
send_interface="org.freedesktop.import1.Transfer"
send_member="Cancel"/>
diff --git a/src/import/pull-common.c b/src/import/pull-common.c
index 38201e46e1..a83cffffa0 100644
--- a/src/import/pull-common.c
+++ b/src/import/pull-common.c
@@ -21,18 +21,28 @@
#include <sys/prctl.h>
-#include "util.h"
-#include "strv.h"
-#include "copy.h"
-#include "rm-rf.h"
+#include "alloc-util.h"
#include "btrfs-util.h"
-#include "capability.h"
-#include "pull-job.h"
-#include "pull-common.h"
+#include "capability-util.h"
+#include "copy.h"
+#include "dirent-util.h"
+#include "escape.h"
+#include "fd-util.h"
+#include "io-util.h"
+#include "path-util.h"
#include "process-util.h"
+#include "pull-common.h"
+#include "pull-job.h"
+#include "rm-rf.h"
#include "signal-util.h"
+#include "siphash24.h"
+#include "string-util.h"
+#include "strv.h"
+#include "util.h"
+#include "web-util.h"
#define FILENAME_ESCAPE "/.#\"\'"
+#define HASH_URL_THRESHOLD_LENGTH (_POSIX_PATH_MAX - 16)
int pull_find_old_etags(
const char *url,
@@ -136,7 +146,7 @@ int pull_make_local_copy(const char *final, const char *image_root, const char *
if (force_local)
(void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
- r = btrfs_subvol_snapshot(final, p, 0);
+ r = btrfs_subvol_snapshot(final, p, BTRFS_SNAPSHOT_QUOTA);
if (r == -ENOTTY) {
r = copy_tree(final, p, false);
if (r < 0)
@@ -149,8 +159,21 @@ int pull_make_local_copy(const char *final, const char *image_root, const char *
return 0;
}
+static int hash_url(const char *url, char **ret) {
+ uint64_t h;
+ static const sd_id128_t k = SD_ID128_ARRAY(df,89,16,87,01,cc,42,30,98,ab,4a,19,a6,a5,63,4f);
+
+ assert(url);
+
+ h = siphash24(url, strlen(url), k.bytes);
+ if (asprintf(ret, "%"PRIx64, h) < 0)
+ return -ENOMEM;
+
+ 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;
+ _cleanup_free_ char *escaped_url = NULL, *escaped_etag = NULL;
char *path;
assert(url);
@@ -164,18 +187,35 @@ int pull_make_path(const char *url, const char *etag, const char *image_root, co
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);
+ path = strjoin(image_root, "/", strempty(prefix), escaped_url, escaped_etag ? "." : "",
+ strempty(escaped_etag), strempty(suffix), NULL);
if (!path)
return -ENOMEM;
+ /* URLs might make the path longer than the maximum allowed length for a file name.
+ * When that happens, a URL hash is used instead. Paths returned by this function
+ * can be later used with tempfn_random() which adds 16 bytes to the resulting name. */
+ if (strlen(path) >= HASH_URL_THRESHOLD_LENGTH) {
+ _cleanup_free_ char *hash = NULL;
+ int r;
+
+ free(path);
+
+ r = hash_url(url, &hash);
+ if (r < 0)
+ return r;
+
+ path = strjoin(image_root, "/", strempty(prefix), hash, escaped_etag ? "." : "",
+ strempty(escaped_etag), strempty(suffix), NULL);
+ if (!path)
+ return -ENOMEM;
+ }
+
*ret = path;
return 0;
}
@@ -334,9 +374,10 @@ int pull_verify(PullJob *main_job,
log_info("SHA256 checksum of %s is valid.", main_job->url);
- assert(!settings_job || settings_job->state == PULL_JOB_DONE);
+ assert(!settings_job || IN_SET(settings_job->state, PULL_JOB_DONE, PULL_JOB_FAILED));
if (settings_job &&
+ settings_job->state == PULL_JOB_DONE &&
settings_job->error == 0 &&
!settings_job->etag_exists) {
diff --git a/src/import/pull-common.h b/src/import/pull-common.h
index 7e6db1862c..ea228bb5c8 100644
--- a/src/import/pull-common.h
+++ b/src/import/pull-common.h
@@ -23,8 +23,8 @@
#include <stdbool.h>
-#include "pull-job.h"
#include "import-util.h"
+#include "pull-job.h"
int pull_make_local_copy(const char *final, const char *root, const char *local, bool force_local);
diff --git a/src/import/pull-dkr.c b/src/import/pull-dkr.c
deleted file mode 100644
index 0dab184af1..0000000000
--- a/src/import/pull-dkr.c
+++ /dev/null
@@ -1,1331 +0,0 @@
-/*-*- 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 <curl/curl.h>
-#include <sys/prctl.h>
-
-#include "sd-daemon.h"
-#include "json.h"
-#include "strv.h"
-#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 "pull-job.h"
-#include "pull-common.h"
-#include "import-common.h"
-#include "pull-dkr.h"
-#include "process-util.h"
-#include "hostname-util.h"
-
-typedef enum DkrProgress {
- DKR_SEARCHING,
- DKR_RESOLVING,
- DKR_METADATA,
- DKR_DOWNLOADING,
- DKR_COPYING,
-} DkrProgress;
-
-struct DkrPull {
- sd_event *event;
- CurlGlue *glue;
-
- char *index_protocol;
- char *index_address;
-
- char *index_url;
- char *image_root;
-
- PullJob *images_job;
- PullJob *tags_job;
- PullJob *ancestry_job;
- PullJob *json_job;
- PullJob *layer_job;
-
- char *name;
- char *reference;
- char *id;
-
- char *response_digest;
- char *response_token;
- char **response_registries;
-
- char **ancestry;
- unsigned n_ancestry;
- unsigned current_ancestry;
-
- DkrPullFinished on_finished;
- void *userdata;
-
- char *local;
- bool force_local;
- bool grow_machine_directory;
-
- char *temp_path;
- char *final_path;
-
- pid_t tar_pid;
-};
-
-#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 HEADER_DIGEST "Do" /* the HTTP header for the manifest digest */ "cker-Content-Digest:"
-#define LAYERS_MAX 127
-
-static void dkr_pull_job_on_finished(PullJob *j);
-
-DkrPull* dkr_pull_unref(DkrPull *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->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) rm_rf(i->temp_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
- free(i->temp_path);
- }
-
- free(i->name);
- free(i->reference);
- free(i->id);
- free(i->response_token);
- 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);
- free(i);
-
- return NULL;
-}
-
-int dkr_pull_new(
- DkrPull **ret,
- sd_event *event,
- const char *index_url,
- const char *image_root,
- DkrPullFinished on_finished,
- void *userdata) {
-
- _cleanup_(dkr_pull_unrefp) DkrPull *i = NULL;
- char *e;
- int r;
-
- assert(ret);
- assert(index_url);
-
- if (!http_url_is_valid(index_url))
- return -EINVAL;
-
- i = new0(DkrPull, 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");
-
- i->index_url = strdup(index_url);
- if (!i->index_url)
- return -ENOMEM;
-
- e = endswith(i->index_url, "/");
- if (e)
- *e = 0;
-
- 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 dkr_pull_report_progress(DkrPull *i, DkrProgress p) {
- unsigned percent;
-
- assert(i);
-
- switch (p) {
-
- case DKR_SEARCHING:
- percent = 0;
- if (i->images_job)
- percent += i->images_job->progress_percent * 5 / 100;
- break;
-
- case DKR_RESOLVING:
- percent = 5;
- if (i->tags_job)
- percent += i->tags_job->progress_percent * 5 / 100;
- break;
-
- case DKR_METADATA:
- percent = 10;
- if (i->ancestry_job)
- percent += i->ancestry_job->progress_percent * 5 / 100;
- if (i->json_job)
- percent += i->json_job->progress_percent * 5 / 100;
- break;
-
- case DKR_DOWNLOADING:
- percent = 20;
- percent += 75 * i->current_ancestry / MAX(1U, i->n_ancestry);
- if (i->layer_job)
- percent += i->layer_job->progress_percent * 75 / MAX(1U, i->n_ancestry) / 100;
-
- break;
-
- case DKR_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 parse_id(const void *payload, size_t size, char **ret) {
- _cleanup_free_ char *buf = NULL, *id = NULL, *other = NULL;
- union json_value v = {};
- void *json_state = NULL;
- const char *p;
- int t;
-
- assert(payload);
- assert(ret);
-
- if (size <= 0)
- return -EBADMSG;
-
- if (memchr(payload, 0, size))
- return -EBADMSG;
-
- buf = strndup(payload, size);
- if (!buf)
- return -ENOMEM;
-
- p = buf;
- t = json_tokenize(&p, &id, &v, &json_state, NULL);
- if (t < 0)
- return t;
- if (t != JSON_STRING)
- return -EBADMSG;
-
- t = json_tokenize(&p, &other, &v, &json_state, NULL);
- if (t < 0)
- return t;
- if (t != JSON_END)
- return -EBADMSG;
-
- if (!dkr_id_is_valid(id))
- return -EBADMSG;
-
- *ret = id;
- id = NULL;
-
- return 0;
-}
-
-static int parse_ancestry(const void *payload, size_t size, char ***ret) {
- _cleanup_free_ char *buf = NULL;
- void *json_state = NULL;
- const char *p;
- enum {
- STATE_BEGIN,
- STATE_ITEM,
- STATE_COMMA,
- STATE_END,
- } state = STATE_BEGIN;
- _cleanup_strv_free_ char **l = NULL;
- size_t n = 0, allocated = 0;
-
- if (size <= 0)
- return -EBADMSG;
-
- if (memchr(payload, 0, size))
- return -EBADMSG;
-
- buf = strndup(payload, size);
- if (!buf)
- return -ENOMEM;
-
- p = buf;
- for (;;) {
- _cleanup_free_ char *str;
- union json_value v = {};
- int t;
-
- t = json_tokenize(&p, &str, &v, &json_state, NULL);
- if (t < 0)
- return t;
-
- switch (state) {
-
- case STATE_BEGIN:
- if (t == JSON_ARRAY_OPEN)
- state = STATE_ITEM;
- else
- return -EBADMSG;
-
- break;
-
- case STATE_ITEM:
- if (t == JSON_STRING) {
- if (!dkr_id_is_valid(str))
- return -EBADMSG;
-
- if (n+1 > LAYERS_MAX)
- return -EFBIG;
-
- if (!GREEDY_REALLOC(l, allocated, n + 2))
- return -ENOMEM;
-
- l[n++] = str;
- str = NULL;
- l[n] = NULL;
-
- state = STATE_COMMA;
-
- } else if (t == JSON_ARRAY_CLOSE)
- state = STATE_END;
- else
- return -EBADMSG;
-
- break;
-
- case STATE_COMMA:
- if (t == JSON_COMMA)
- state = STATE_ITEM;
- else if (t == JSON_ARRAY_CLOSE)
- state = STATE_END;
- else
- return -EBADMSG;
- break;
-
- case STATE_END:
- if (t == JSON_END) {
-
- if (strv_isempty(l))
- return -EBADMSG;
-
- if (!strv_is_uniq(l))
- return -EBADMSG;
-
- l = strv_reverse(l);
-
- *ret = l;
- l = NULL;
- return 0;
- } else
- return -EBADMSG;
- }
-
- }
-}
-
-static const char *dkr_pull_current_layer(DkrPull *i) {
- assert(i);
-
- if (strv_isempty(i->ancestry))
- return NULL;
-
- return i->ancestry[i->current_ancestry];
-}
-
-static const char *dkr_pull_current_base_layer(DkrPull *i) {
- assert(i);
-
- if (strv_isempty(i->ancestry))
- return NULL;
-
- if (i->current_ancestry <= 0)
- return NULL;
-
- return i->ancestry[i->current_ancestry-1];
-}
-
-static int dkr_pull_add_token(DkrPull *i, PullJob *j) {
- const char *t;
-
- assert(i);
- assert(j);
-
- if (i->response_token)
- t = strjoina("Authorization: Token ", i->response_token);
- else
- t = HEADER_TOKEN " true";
-
- j->request_header = curl_slist_new("Accept: application/json", t, NULL);
- if (!j->request_header)
- return -ENOMEM;
-
- return 0;
-}
-
-static int dkr_pull_add_bearer_token(DkrPull *i, PullJob *j) {
- const char *t = NULL;
-
- assert(i);
- assert(j);
-
- 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 != PULL_JOB_DONE)
- return false;
-
- if (!i->ancestry_job || i->ancestry_job->state != PULL_JOB_DONE)
- return false;
-
- if (i->json_job && i->json_job->state != PULL_JOB_DONE)
- return false;
-
- if (i->layer_job && i->layer_job->state != PULL_JOB_DONE)
- return false;
-
- if (dkr_pull_current_layer(i))
- return false;
-
- return true;
-}
-
-static int dkr_pull_make_local_copy(DkrPull *i, DkrPullVersion version) {
- int r;
- _cleanup_free_ char *p = NULL;
-
- assert(i);
-
- if (!i->local)
- return 0;
-
- if (!i->final_path) {
- i->final_path = strjoin(i->image_root, "/.dkr-", i->id, NULL);
- if (!i->final_path)
- return log_oom();
- }
-
- 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_pull_job_on_open_disk(PullJob *j) {
- const char *base;
- DkrPull *i;
- int r;
-
- assert(j);
- assert(j->userdata);
-
- i = j->userdata;
- assert(i->layer_job == j);
- assert(i->final_path);
- assert(!i->temp_path);
- assert(i->tar_pid <= 0);
-
- r = tempfn_random(i->final_path, NULL, &i->temp_path);
- if (r < 0)
- return log_oom();
-
- mkdir_parents_label(i->temp_path, 0700);
-
- 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, 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_x(i->temp_path, &i->tar_pid);
- if (j->disk_fd < 0)
- return j->disk_fd;
-
- return 0;
-}
-
-static void dkr_pull_job_on_progress(PullJob *j) {
- DkrPull *i;
-
- assert(j);
- assert(j->userdata);
-
- i = j->userdata;
-
- dkr_pull_report_progress(
- i,
- j == i->images_job ? DKR_SEARCHING :
- j == i->tags_job ? DKR_RESOLVING :
- j == i->ancestry_job || j == i->json_job ? DKR_METADATA :
- DKR_DOWNLOADING);
-}
-
-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;
-
- 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 */
-
- 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++;
-
- path = mfree(path);
- }
-
- 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 */
-
- 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++;
-
- path = mfree(path);
- }
-
- log_info("Pulling layer %s...", layer);
-
- i->final_path = path;
- path = NULL;
-
- url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v1/images/", layer, "/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_token(i, i->layer_job);
- if (r < 0)
- return log_oom();
-
- 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 = 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_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, false)) {
- 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_ 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);
- 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->tags_job)
- log_error_errno(j->error, "Failed to retrieve tags list.");
- else if (j == i->ancestry_job)
- log_error_errno(j->error, "Failed to retrieve ancestry list.");
- 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(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_pull_add_token(i, i->tags_job);
- if (r < 0) {
- log_oom();
- goto finish;
- }
-
- i->tags_job->on_finished = dkr_pull_job_on_finished;
- 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;
- char *id = NULL;
-
- assert(!i->ancestry_job);
- assert(!i->json_job);
- assert(!i->layer_job);
-
- r = parse_id(j->payload, j->payload_size, &id);
- if (r < 0) {
- log_error_errno(r, "Failed to parse JSON id.");
- goto finish;
- }
-
- free(i->id);
- i->id = id;
-
- log_info("Tag lookup succeeded, resolved to layer %s.", i->id);
- dkr_pull_report_progress(i, DKR_METADATA);
-
- url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v1/images/", i->id, "/ancestry");
- 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_token(i, i->ancestry_job);
- if (r < 0) {
- log_oom();
- goto finish;
- }
-
- 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 = 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_pull_add_token(i, i->json_job);
- if (r < 0) {
- log_oom();
- goto finish;
- }
-
- i->json_job->on_finished = dkr_pull_job_on_finished;
- i->json_job->on_progress = dkr_pull_job_on_progress;
-
- r = pull_job_begin(i->ancestry_job);
- if (r < 0) {
- log_error_errno(r, "Failed to start ancestry job: %m");
- goto finish;
- }
-
- r = pull_job_begin(i->json_job);
- if (r < 0) {
- log_error_errno(r, "Failed to start json job: %m");
- goto finish;
- }
-
- } else if (i->ancestry_job == j) {
- char **ancestry = NULL, **k;
- unsigned n;
-
- assert(!i->layer_job);
-
- r = parse_ancestry(j->payload, j->payload_size, &ancestry);
- if (r < 0) {
- log_error_errno(r, "Failed to parse JSON id.");
- goto finish;
- }
-
- n = strv_length(ancestry);
- if (n <= 0 || !streq(ancestry[n-1], i->id)) {
- log_error("Ancestry doesn't end in main layer.");
- strv_free(ancestry);
- r = -EBADMSG;
- goto finish;
- }
-
- log_info("Ancestor lookup succeeded, requires layers:\n");
- STRV_FOREACH(k, ancestry)
- log_info("\t%s", *k);
-
- strv_free(i->ancestry);
- i->ancestry = ancestry;
- i->n_ancestry = n;
- i->current_ancestry = 0;
-
- dkr_pull_report_progress(i, DKR_DOWNLOADING);
-
- r = dkr_pull_pull_layer(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);
- i->temp_path = mfree(i->temp_path);
- i->final_path = mfree(i->final_path);
-
- i->current_ancestry ++;
- 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_pull_is_done(i))
- return;
-
- dkr_pull_report_progress(i, DKR_COPYING);
-
- 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);
- else
- sd_event_exit(i->event, r);
-}
-
-static int get_protocol_address(char **protocol, char **address, const char *url) {
- const char *sep, *dot;
- _cleanup_free_ char *a = NULL, *p = NULL;
-
- sep = strstr(url, "://");
- if (!sep)
- return -EINVAL;
-
- dot = strrchr(url, '.');
- if (!dot)
- return -EINVAL;
- dot--;
-
- p = strndup(url, (sep - url) + 3);
- if (!p)
- return log_oom();
-
- while (dot > (sep + 3) && *dot != '.')
- dot--;
-
- a = strdup(dot + 1);
- if (!a)
- return log_oom();
-
- *address = a;
- *protocol = p;
- a = p = NULL;
-
- return 0;
-}
-
-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;
-
- assert(i);
-
- if (!dkr_name_is_valid(name))
- return -EINVAL;
-
- if (reference && !dkr_ref_is_valid(reference))
- return -EINVAL;
-
- if (local && !machine_name_is_valid(local))
- return -EINVAL;
-
- if (i->images_job)
- return -EBUSY;
-
- 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)
- return r;
- i->force_local = force_local;
-
- r = free_and_strdup(&i->name, name);
- if (r < 0)
- return r;
- r = free_and_strdup(&i->reference, reference);
- if (r < 0)
- return r;
-
- url = strjoina(i->index_url, "/v1/repositories/", name, "/images");
-
- r = pull_job_new(&i->images_job, url, i->glue, i);
- if (r < 0)
- return r;
-
- r = dkr_pull_add_token(i, i->images_job);
- if (r < 0)
- return r;
-
- 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 pull_job_begin(i->images_job);
-}
diff --git a/src/import/pull-dkr.h b/src/import/pull-dkr.h
deleted file mode 100644
index 33d18cb394..0000000000
--- a/src/import/pull-dkr.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*-*- 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/pull-job.c b/src/import/pull-job.c
index 42939f2104..824fa246ec 100644
--- a/src/import/pull-job.c
+++ b/src/import/pull-job.c
@@ -21,9 +21,16 @@
#include <sys/xattr.h>
-#include "strv.h"
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "hexdecoct.h"
+#include "io-util.h"
#include "machine-pool.h"
+#include "parse-util.h"
#include "pull-job.h"
+#include "string-util.h"
+#include "strv.h"
+#include "xattr-util.h"
PullJob* pull_job_unref(PullJob *j) {
if (!j)
diff --git a/src/import/pull-job.h b/src/import/pull-job.h
index 1777bf1c33..56a74a34ef 100644
--- a/src/import/pull-job.h
+++ b/src/import/pull-job.h
@@ -23,9 +23,9 @@
#include <gcrypt.h>
-#include "macro.h"
#include "curl-util.h"
#include "import-compress.h"
+#include "macro.h"
typedef struct PullJob PullJob;
diff --git a/src/import/pull-raw.c b/src/import/pull-raw.c
index 0e77197e34..03bfb51756 100644
--- a/src/import/pull-raw.c
+++ b/src/import/pull-raw.c
@@ -19,28 +19,36 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/xattr.h>
-#include <linux/fs.h>
#include <curl/curl.h>
+#include <linux/fs.h>
+#include <sys/xattr.h>
#include "sd-daemon.h"
-#include "utf8.h"
-#include "strv.h"
-#include "copy.h"
+
+#include "alloc-util.h"
#include "btrfs-util.h"
-#include "util.h"
+#include "chattr-util.h"
+#include "copy.h"
+#include "curl-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "hostname-util.h"
+#include "import-common.h"
+#include "import-util.h"
#include "macro.h"
#include "mkdir.h"
-#include "rm-rf.h"
#include "path-util.h"
-#include "hostname-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-job.h"
#include "pull-raw.h"
+#include "qcow2-util.h"
+#include "rm-rf.h"
+#include "string-util.h"
+#include "strv.h"
+#include "utf8.h"
+#include "util.h"
+#include "web-util.h"
typedef enum RawProgress {
RAW_DOWNLOADING,
@@ -236,7 +244,7 @@ static int raw_pull_maybe_convert_qcow2(RawPull *i) {
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_warning_errno(r, "Failed to set file attributes on %s: %m", t);
log_info("Unpacking QCOW2 file.");
@@ -312,7 +320,7 @@ static int raw_pull_make_local_copy(RawPull *i) {
* 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);
+ log_warning_errno(r, "Failed to set file attributes on %s: %m", tp);
r = copy_bytes(i->raw_job->disk_fd, dfd, (uint64_t) -1, true);
if (r < 0) {
@@ -327,8 +335,9 @@ static int raw_pull_make_local_copy(RawPull *i) {
r = rename(tp, p);
if (r < 0) {
+ r = log_error_errno(errno, "Failed to move writable image into place: %m");
unlink(tp);
- return log_error_errno(errno, "Failed to move writable image into place: %m");
+ return r;
}
log_info("Created new local image '%s'.", i->local);
@@ -349,9 +358,9 @@ static int raw_pull_make_local_copy(RawPull *i) {
if (r == -EEXIST)
log_warning_errno(r, "Settings file %s already exists, not replacing.", local_settings);
else if (r < 0 && r != -ENOENT)
- log_warning_errno(r, "Failed to copy settings files %s: %m", local_settings);
-
- log_info("Create new settings file '%s.nspawn'", i->local);
+ log_warning_errno(r, "Failed to copy settings files %s, ignoring: %m", local_settings);
+ else
+ log_info("Created new settings file '%s.nspawn'", i->local);
}
return 0;
@@ -503,7 +512,7 @@ static int raw_pull_job_on_open_disk_raw(PullJob *j) {
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);
+ log_warning_errno(r, "Failed to set file attributes on %s: %m", i->temp_path);
return 0;
}
diff --git a/src/import/pull-raw.h b/src/import/pull-raw.h
index b03b4f5c92..0e4e1daf0e 100644
--- a/src/import/pull-raw.h
+++ b/src/import/pull-raw.h
@@ -22,8 +22,9 @@
***/
#include "sd-event.h"
-#include "macro.h"
+
#include "import-util.h"
+#include "macro.h"
typedef struct RawPull RawPull;
diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c
index 563765d83d..2e48167c54 100644
--- a/src/import/pull-tar.c
+++ b/src/import/pull-tar.c
@@ -19,27 +19,34 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/prctl.h>
#include <curl/curl.h>
+#include <sys/prctl.h>
#include "sd-daemon.h"
-#include "utf8.h"
-#include "strv.h"
-#include "copy.h"
+
+#include "alloc-util.h"
#include "btrfs-util.h"
-#include "util.h"
+#include "copy.h"
+#include "curl-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "hostname-util.h"
+#include "import-common.h"
+#include "import-util.h"
#include "macro.h"
#include "mkdir.h"
-#include "rm-rf.h"
#include "path-util.h"
#include "process-util.h"
-#include "hostname-util.h"
-#include "import-util.h"
-#include "import-common.h"
-#include "curl-util.h"
-#include "pull-job.h"
#include "pull-common.h"
+#include "pull-job.h"
#include "pull-tar.h"
+#include "rm-rf.h"
+#include "string-util.h"
+#include "strv.h"
+#include "utf8.h"
+#include "util.h"
+#include "web-util.h"
typedef enum TarProgress {
TAR_DOWNLOADING,
@@ -247,9 +254,9 @@ static int tar_pull_make_local_copy(TarPull *i) {
if (r == -EEXIST)
log_warning_errno(r, "Settings file %s already exists, not replacing.", local_settings);
else if (r < 0 && r != -ENOENT)
- log_warning_errno(r, "Failed to copy settings files %s: %m", local_settings);
-
- log_info("Create new settings file '%s.nspawn'", i->local);
+ log_warning_errno(r, "Failed to copy settings files %s, ignoring: %m", local_settings);
+ else
+ log_info("Created new settings file '%s.nspawn'", i->local);
}
return 0;
@@ -409,7 +416,9 @@ static int tar_pull_job_on_open_disk_tar(PullJob *j) {
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);
+ return log_error_errno(r, "Failed to create subvolume %s: %m", i->temp_path);
+ else
+ (void) import_assign_pool_quota_and_warn(i->temp_path);
j->disk_fd = import_fork_tar_x(i->temp_path, &i->tar_pid);
if (j->disk_fd < 0)
diff --git a/src/import/pull-tar.h b/src/import/pull-tar.h
index 420845ae50..9f02f1ec71 100644
--- a/src/import/pull-tar.h
+++ b/src/import/pull-tar.h
@@ -22,8 +22,9 @@
***/
#include "sd-event.h"
-#include "macro.h"
+
#include "import-util.h"
+#include "macro.h"
typedef struct TarPull TarPull;
diff --git a/src/import/pull.c b/src/import/pull.c
index 98c22aeec9..e0631bdeaf 100644
--- a/src/import/pull.c
+++ b/src/import/pull.c
@@ -22,21 +22,22 @@
#include <getopt.h>
#include "sd-event.h"
-#include "event-util.h"
-#include "verbs.h"
-#include "build.h"
-#include "signal-util.h"
+
+#include "alloc-util.h"
#include "hostname-util.h"
-#include "machine-image.h"
#include "import-util.h"
-#include "pull-tar.h"
+#include "machine-image.h"
+#include "parse-util.h"
#include "pull-raw.h"
-#include "pull-dkr.h"
+#include "pull-tar.h"
+#include "signal-util.h"
+#include "string-util.h"
+#include "verbs.h"
+#include "web-util.h"
static bool arg_force = false;
static const char *arg_image_root = "/var/lib/machines";
static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
-static const char* arg_dkr_index_url = DEFAULT_DKR_INDEX_URL;
static bool arg_settings = true;
static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
@@ -57,7 +58,7 @@ static void on_tar_finished(TarPull *pull, int error, void *userdata) {
static int pull_tar(int argc, char *argv[], void *userdata) {
_cleanup_(tar_pull_unrefp) TarPull *pull = NULL;
- _cleanup_event_unref_ sd_event *event = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
const char *url, *local;
_cleanup_free_ char *l = NULL, *ll = NULL;
int r;
@@ -143,7 +144,7 @@ static void on_raw_finished(RawPull *pull, int error, void *userdata) {
static int pull_raw(int argc, char *argv[], void *userdata) {
_cleanup_(raw_pull_unrefp) RawPull *pull = NULL;
- _cleanup_event_unref_ sd_event *event = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
const char *url, *local;
_cleanup_free_ char *l = NULL, *ll = NULL;
int r;
@@ -217,114 +218,6 @@ static int pull_raw(int argc, char *argv[], void *userdata) {
return -r;
}
-static void on_dkr_finished(DkrPull *pull, int error, void *userdata) {
- sd_event *event = userdata;
- assert(pull);
-
- if (error == 0)
- log_info("Operation completed successfully.");
-
- sd_event_exit(event, abs(error));
-}
-
-static int pull_dkr(int argc, char *argv[], void *userdata) {
- _cleanup_(dkr_pull_unrefp) DkrPull *pull = NULL;
- _cleanup_event_unref_ sd_event *event = NULL;
- const char *name, *reference, *local, *digest;
- int r;
-
- if (!arg_dkr_index_url) {
- log_error("Please specify an index URL with --dkr-index-url=");
- return -EINVAL;
- }
-
- if (arg_verify != IMPORT_VERIFY_NO) {
- log_error("Pulls from dkr do not support image verification, please pass --verify=no.");
- return -EINVAL;
- }
-
- digest = strchr(argv[1], '@');
- if (digest) {
- reference = digest + 1;
- name = strndupa(argv[1], digest - argv[1]);
- } else {
- reference = strchr(argv[1], ':');
- if (reference) {
- name = strndupa(argv[1], reference - argv[1]);
- reference++;
- } else {
- name = argv[1];
- reference = "latest";
- }
- }
-
- if (!dkr_name_is_valid(name)) {
- log_error("Remote name '%s' is not valid.", name);
- return -EINVAL;
- }
-
- if (!dkr_ref_is_valid(reference)) {
- log_error("Tag name '%s' is not valid.", reference);
- return -EINVAL;
- }
-
- if (argc >= 3)
- local = argv[2];
- else {
- local = strchr(name, '/');
- if (local)
- local++;
- else
- local = name;
- }
-
- if (isempty(local) || streq(local, "-"))
- local = NULL;
-
- if (local) {
- 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;
- }
- }
-
- log_info("Pulling '%s' with reference '%s', saving as '%s'.", name, reference, local);
- } else
- log_info("Pulling '%s' with reference '%s'.", name, reference);
-
- 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, NULL, SIGTERM, SIGINT, -1) >= 0);
- (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
- (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
-
- 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 puller: %m");
-
- 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");
-
- 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"
@@ -335,12 +228,10 @@ static int help(int argc, char *argv[], void *userdata) {
" --verify=MODE Verify downloaded image, one of: 'no',\n"
" 'checksum', 'signature'\n"
" --settings=BOOL Download settings file with image\n"
- " --image-root=PATH Image root directory\n"
- " --dkr-index-url=URL Specify index URL to use for downloads\n\n"
+ " --image-root=PATH Image root directory\n\n"
"Commands:\n"
" tar URL [NAME] Download a TAR image\n"
- " raw URL [NAME] Download a RAW image\n"
- " dkr REMOTE [NAME] Download a DKR image\n",
+ " raw URL [NAME] Download a RAW image\n",
program_invocation_short_name);
return 0;
@@ -351,7 +242,6 @@ static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_FORCE,
- ARG_DKR_INDEX_URL,
ARG_IMAGE_ROOT,
ARG_VERIFY,
ARG_SETTINGS,
@@ -361,7 +251,6 @@ static int parse_argv(int argc, char *argv[]) {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "force", no_argument, NULL, ARG_FORCE },
- { "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL },
{ "image-root", required_argument, NULL, ARG_IMAGE_ROOT },
{ "verify", required_argument, NULL, ARG_VERIFY },
{ "settings", required_argument, NULL, ARG_SETTINGS },
@@ -381,23 +270,12 @@ static int parse_argv(int argc, char *argv[]) {
return help(0, NULL, NULL);
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case ARG_FORCE:
arg_force = true;
break;
- case ARG_DKR_INDEX_URL:
- if (!http_url_is_valid(optarg)) {
- log_error("Index URL is not valid: %s", optarg);
- return -EINVAL;
- }
-
- arg_dkr_index_url = optarg;
- break;
-
case ARG_IMAGE_ROOT:
arg_image_root = optarg;
break;
@@ -435,7 +313,6 @@ static int pull_main(int argc, char *argv[]) {
{ "help", VERB_ANY, VERB_ANY, 0, help },
{ "tar", 2, 3, 0, pull_tar },
{ "raw", 2, 3, 0, pull_raw },
- { "dkr", 2, 3, 0, pull_dkr },
{}
};
diff --git a/src/import/qcow2-util.c b/src/import/qcow2-util.c
index fd3cf1b0e3..47dabaa86e 100644
--- a/src/import/qcow2-util.c
+++ b/src/import/qcow2-util.c
@@ -21,10 +21,11 @@
#include <zlib.h>
-#include "util.h"
-#include "sparse-endian.h"
-#include "qcow2-util.h"
+#include "alloc-util.h"
#include "btrfs-util.h"
+#include "qcow2-util.h"
+#include "sparse-endian.h"
+#include "util.h"
#define QCOW2_MAGIC 0x514649fb
diff --git a/src/import/test-qcow2.c b/src/import/test-qcow2.c
index 9a6c3e8b35..4b60079619 100644
--- a/src/import/test-qcow2.c
+++ b/src/import/test-qcow2.c
@@ -19,10 +19,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "fd-util.h"
#include "log.h"
-#include "util.h"
-
#include "qcow2-util.h"
+#include "util.h"
int main(int argc, char *argv[]) {
_cleanup_close_ int sfd = -1, dfd = -1;
diff --git a/src/initctl/initctl.c b/src/initctl/initctl.c
index 6d08db74ef..7e51735df0 100644
--- a/src/initctl/initctl.c
+++ b/src/initctl/initctl.c
@@ -19,24 +19,26 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
+#include <ctype.h>
#include <errno.h>
-#include <unistd.h>
+#include <stdio.h>
#include <sys/epoll.h>
-#include <ctype.h>
+#include <unistd.h>
-#include "sd-daemon.h"
#include "sd-bus.h"
+#include "sd-daemon.h"
-#include "util.h"
-#include "log.h"
-#include "list.h"
-#include "initreq.h"
-#include "special.h"
-#include "bus-util.h"
+#include "alloc-util.h"
#include "bus-error.h"
+#include "bus-util.h"
#include "def.h"
+#include "fd-util.h"
#include "formats-util.h"
+#include "initreq.h"
+#include "list.h"
+#include "log.h"
+#include "special.h"
+#include "util.h"
#define SERVER_FD_MAX 16
#define TIMEOUT_MSEC ((int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC))
@@ -99,7 +101,7 @@ static const char *translate_runlevel(int runlevel, bool *isolate) {
static void change_runlevel(Server *s, int runlevel) {
const char *target;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
const char *mode;
bool isolate = false;
int r;
@@ -210,8 +212,7 @@ static int fifo_process(Fifo *f) {
if (errno == EAGAIN)
return 0;
- log_warning_errno(errno, "Failed to read from fifo: %m");
- return -errno;
+ return log_warning_errno(errno, "Failed to read from fifo: %m");
}
f->bytes_read += l;
@@ -318,7 +319,7 @@ static int server_init(Server *s, unsigned n_sockets) {
s->n_fifos ++;
}
- r = bus_open_system_systemd(&s->bus);
+ r = bus_connect_system_systemd(&s->bus);
if (r < 0) {
log_error_errno(r, "Failed to get D-Bus connection: %m");
r = -EIO;
diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c
index 4e5572db0b..f5fe165fa3 100644
--- a/src/journal-remote/journal-gatewayd.c
+++ b/src/journal-remote/journal-gatewayd.c
@@ -19,30 +19,33 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
#include <fcntl.h>
#include <getopt.h>
-
-#include <microhttpd.h>
-
#ifdef HAVE_GNUTLS
#include <gnutls/gnutls.h>
#endif
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
-#include "sd-journal.h"
-#include "sd-daemon.h"
#include "sd-bus.h"
-#include "log.h"
-#include "util.h"
+#include "sd-daemon.h"
+#include "sd-journal.h"
+
+#include "alloc-util.h"
#include "bus-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "hostname-util.h"
+#include "log.h"
#include "logs-show.h"
#include "microhttpd-util.h"
-#include "build.h"
-#include "fileio.h"
+#include "parse-util.h"
#include "sigbus.h"
-#include "hostname-util.h"
+#include "util.h"
+
+#define JOURNAL_WAIT_TIMEOUT (10*USEC_PER_SEC)
static char *arg_key_pem = NULL;
static char *arg_cert_pem = NULL;
@@ -180,11 +183,13 @@ static ssize_t request_reader_entries(
} else if (r == 0) {
if (m->follow) {
- r = sd_journal_wait(m->journal, (uint64_t) -1);
+ r = sd_journal_wait(m->journal, (uint64_t) JOURNAL_WAIT_TIMEOUT);
if (r < 0) {
log_error_errno(r, "Couldn't wait for journal event: %m");
return MHD_CONTENT_READER_END_WITH_ERROR;
}
+ if (r == SD_JOURNAL_NOP)
+ break;
continue;
}
@@ -240,6 +245,8 @@ static ssize_t request_reader_entries(
}
n = m->size - pos;
+ if (n < 1)
+ return 0;
if (n > max)
n = max;
@@ -693,7 +700,7 @@ static int request_handler_file(
if (fstat(fd, &st) < 0)
return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to stat file: %m\n");
- response = MHD_create_response_from_fd_at_offset(st.st_size, fd, 0);
+ response = MHD_create_response_from_fd_at_offset64(st.st_size, fd, 0);
if (!response)
return respond_oom(connection);
@@ -708,7 +715,7 @@ static int request_handler_file(
}
static int get_virtualization(char **v) {
- _cleanup_bus_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
char *b = NULL;
int r;
@@ -833,7 +840,7 @@ static int request_handler(
assert(method);
if (!streq(method, "GET"))
- return mhd_respond(connection, MHD_HTTP_METHOD_NOT_ACCEPTABLE,
+ return mhd_respond(connection, MHD_HTTP_NOT_ACCEPTABLE,
"Unsupported method.\n");
@@ -909,9 +916,7 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case ARG_KEY:
if (arg_key_pem) {
@@ -1014,7 +1019,22 @@ int main(int argc, char *argv[]) {
{ MHD_OPTION_END, 0, NULL },
{ MHD_OPTION_END, 0, NULL }};
int opts_pos = 2;
- int flags = MHD_USE_THREAD_PER_CONNECTION|MHD_USE_POLL|MHD_USE_DEBUG;
+
+ /* We force MHD_USE_PIPE_FOR_SHUTDOWN here, in order
+ * to make sure libmicrohttpd doesn't use shutdown()
+ * on our listening socket, which would break socket
+ * re-activation. See
+ *
+ * https://lists.gnu.org/archive/html/libmicrohttpd/2015-09/msg00014.html
+ * https://github.com/systemd/systemd/pull/1286
+ */
+
+ int flags =
+ MHD_USE_DEBUG |
+ MHD_USE_DUAL_STACK |
+ MHD_USE_PIPE_FOR_SHUTDOWN |
+ MHD_USE_POLL |
+ MHD_USE_THREAD_PER_CONNECTION;
if (n > 0)
opts[opts_pos++] = (struct MHD_OptionItem)
diff --git a/src/journal-remote/journal-remote-parse.c b/src/journal-remote/journal-remote-parse.c
index 2e0f78701a..3ff40228a0 100644
--- a/src/journal-remote/journal-remote-parse.c
+++ b/src/journal-remote/journal-remote-parse.c
@@ -19,8 +19,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "alloc-util.h"
+#include "fd-util.h"
#include "journal-remote-parse.h"
#include "journald-native.h"
+#include "parse-util.h"
+#include "string-util.h"
#define LINE_CHUNK 8*1024u
diff --git a/src/journal-remote/journal-remote-parse.h b/src/journal-remote/journal-remote-parse.h
index 14bfadc132..58cb5e70df 100644
--- a/src/journal-remote/journal-remote-parse.h
+++ b/src/journal-remote/journal-remote-parse.h
@@ -22,6 +22,7 @@
#pragma once
#include "sd-event.h"
+
#include "journal-remote-write.h"
typedef enum {
diff --git a/src/journal-remote/journal-remote-write.c b/src/journal-remote/journal-remote-write.c
index 40f4ff8e58..d8250378b0 100644
--- a/src/journal-remote/journal-remote-write.c
+++ b/src/journal-remote/journal-remote-write.c
@@ -19,6 +19,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "alloc-util.h"
#include "journal-remote.h"
int iovw_put(struct iovec_wrapper *iovw, void* data, size_t len) {
diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c
index d6ebca20ec..2126606661 100644
--- a/src/journal-remote/journal-remote.c
+++ b/src/journal-remote/journal-remote.c
@@ -21,31 +21,39 @@
#include <errno.h>
#include <fcntl.h>
+#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/prctl.h>
#include <sys/socket.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 "build.h"
-#include "macro.h"
-#include "strv.h"
-#include "fileio.h"
-#include "conf-parser.h"
#ifdef HAVE_GNUTLS
#include <gnutls/gnutls.h>
#endif
-#include "journal-remote.h"
+#include "sd-daemon.h"
+
+#include "alloc-util.h"
+#include "conf-parser.h"
+#include "def.h"
+#include "escape.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "journal-file.h"
#include "journal-remote-write.h"
+#include "journal-remote.h"
+#include "journald-native.h"
+#include "macro.h"
+#include "parse-util.h"
+#include "signal-util.h"
+#include "socket-util.h"
+#include "stat-util.h"
+#include "stdio-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "strv.h"
#define REMOTE_JOURNAL_PATH "/var/log/journal/remote"
@@ -138,7 +146,7 @@ static int spawn_curl(const char* url) {
r = spawn_child("curl", argv);
if (r < 0)
- log_error_errno(errno, "Failed to spawn curl: %m");
+ log_error_errno(r, "Failed to spawn curl: %m");
return r;
}
@@ -157,7 +165,7 @@ static int spawn_getter(const char *getter, const char *url) {
r = spawn_child(words[0], words);
if (r < 0)
- log_error_errno(errno, "Failed to spawn getter %s: %m", getter);
+ log_error_errno(r, "Failed to spawn getter %s: %m", getter);
return r;
}
@@ -579,7 +587,7 @@ static int request_handler(
*connection_cls);
if (!streq(method, "POST"))
- return mhd_respond(connection, MHD_HTTP_METHOD_NOT_ACCEPTABLE,
+ return mhd_respond(connection, MHD_HTTP_NOT_ACCEPTABLE,
"Unsupported method.\n");
if (!streq(url, "/upload"))
@@ -641,16 +649,18 @@ static int setup_microhttpd_server(RemoteServer *s,
{ MHD_OPTION_NOTIFY_COMPLETED, (intptr_t) request_meta_free},
{ MHD_OPTION_EXTERNAL_LOGGER, (intptr_t) microhttpd_logger},
{ MHD_OPTION_LISTEN_SOCKET, fd},
+ { MHD_OPTION_CONNECTION_MEMORY_LIMIT, DATA_SIZE_MAX},
{ MHD_OPTION_END},
{ MHD_OPTION_END},
{ MHD_OPTION_END},
{ MHD_OPTION_END}};
- int opts_pos = 3;
+ int opts_pos = 4;
int flags =
MHD_USE_DEBUG |
- MHD_USE_PEDANTIC_CHECKS |
+ MHD_USE_DUAL_STACK |
MHD_USE_EPOLL_LINUX_ONLY |
- MHD_USE_DUAL_STACK;
+ MHD_USE_PEDANTIC_CHECKS |
+ MHD_USE_PIPE_FOR_SHUTDOWN;
const union MHD_DaemonInfo *info;
int r, epoll_fd;
@@ -954,7 +964,7 @@ static int remoteserver_init(RemoteServer *s,
}
if (s->active == 0) {
- log_error("Zarro sources specified");
+ log_error("Zero sources specified");
return -EINVAL;
}
@@ -1171,6 +1181,7 @@ static DEFINE_CONFIG_PARSE_ENUM(config_parse_write_split_mode,
static int parse_config(void) {
const ConfigTableItem items[] = {
+ { "Remote", "Seal", config_parse_bool, 0, &arg_seal },
{ "Remote", "SplitMode", config_parse_write_split_mode, 0, &arg_split_mode },
{ "Remote", "ServerKeyFile", config_parse_path, 0, &arg_key },
{ "Remote", "ServerCertificateFile", config_parse_path, 0, &arg_cert },
@@ -1178,7 +1189,7 @@ static int parse_config(void) {
{}};
return config_parse_many(PKGSYSCONFDIR "/journal-remote.conf",
- CONF_DIRS_NULSTR("systemd/journal-remote.conf"),
+ CONF_PATHS_NULSTR("systemd/journal-remote.conf.d"),
"Remote\0", config_item_table_lookup, items,
false, NULL);
}
@@ -1259,9 +1270,7 @@ static int parse_argv(int argc, char *argv[]) {
return 0 /* done */;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0 /* done */;
+ return version();
case ARG_URL:
if (arg_url) {
@@ -1409,18 +1418,21 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_GNUTLS_LOG: {
#ifdef HAVE_GNUTLS
- const char *word, *state;
- size_t size;
+ const char* p = optarg;
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
- FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) {
- char *cat;
+ r = extract_first_word(&p, &word, ",", 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --gnutls-log= argument: %m");
- cat = strndup(word, size);
- if (!cat)
- return log_oom();
+ if (r == 0)
+ break;
- if (strv_consume(&arg_gnutls_log, cat) < 0)
+ if (strv_push(&arg_gnutls_log, word) < 0)
return log_oom();
+
+ word = NULL;
}
break;
#else
diff --git a/src/journal-remote/journal-remote.conf.in b/src/journal-remote/journal-remote.conf.in
index 3e32f34def..7122d63362 100644
--- a/src/journal-remote/journal-remote.conf.in
+++ b/src/journal-remote/journal-remote.conf.in
@@ -1,4 +1,5 @@
[Remote]
+# Seal=false
# SplitMode=host
# ServerKeyFile=@CERTIFICATEROOT@/private/journal-remote.pem
# ServerCertificateFile=@CERTIFICATEROOT@/certs/journal-remote.pem
diff --git a/src/journal-remote/journal-remote.h b/src/journal-remote/journal-remote.h
index 6c2ccb9735..fd81a1c592 100644
--- a/src/journal-remote/journal-remote.h
+++ b/src/journal-remote/journal-remote.h
@@ -23,11 +23,11 @@
#include "sd-event.h"
-#include "hashmap.h"
-#include "microhttpd-util.h"
+#include "hashmap.h"
#include "journal-remote-parse.h"
#include "journal-remote-write.h"
+#include "microhttpd-util.h"
typedef struct MHDDaemonWrapper MHDDaemonWrapper;
diff --git a/src/journal-remote/journal-upload-journal.c b/src/journal-remote/journal-upload-journal.c
index 6b3ad924a7..f9d2385215 100644
--- a/src/journal-remote/journal-upload-journal.c
+++ b/src/journal-remote/journal-upload-journal.c
@@ -1,11 +1,32 @@
-#include <stdbool.h>
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 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 <curl/curl.h>
+#include <stdbool.h>
-#include "util.h"
+#include "alloc-util.h"
+#include "journal-upload.h"
#include "log.h"
#include "utf8.h"
-#include "journal-upload.h"
+#include "util.h"
/**
* Write up to size bytes to buf. Return negative on error, and number of
@@ -291,6 +312,9 @@ void close_journal_input(Uploader *u) {
static int process_journal_input(Uploader *u, int skip) {
int r;
+ if (u->uploading)
+ return 0;
+
r = sd_journal_next_skip(u->journal, skip);
if (r < 0)
return log_error_errno(r, "Failed to skip to next entry: %m");
@@ -328,10 +352,8 @@ static int dispatch_journal_input(sd_event_source *event,
assert(u);
- if (u->uploading) {
- log_warning("dispatch_journal_input called when uploading, ignoring.");
+ if (u->uploading)
return 0;
- }
log_debug("Detected journal input, checking for new data.");
return check_journal_input(u);
diff --git a/src/journal-remote/journal-upload.c b/src/journal-remote/journal-upload.c
index 311bd3fdda..6302266ccb 100644
--- a/src/journal-remote/journal-upload.c
+++ b/src/journal-remote/journal-upload.c
@@ -19,23 +19,29 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
#include <curl/curl.h>
-#include <sys/stat.h>
#include <fcntl.h>
#include <getopt.h>
+#include <stdio.h>
+#include <sys/stat.h>
#include "sd-daemon.h"
-#include "log.h"
-#include "util.h"
-#include "build.h"
+
+#include "alloc-util.h"
+#include "conf-parser.h"
+#include "def.h"
+#include "fd-util.h"
#include "fileio.h"
+#include "formats-util.h"
+#include "glob-util.h"
+#include "journal-upload.h"
+#include "log.h"
#include "mkdir.h"
-#include "conf-parser.h"
+#include "parse-util.h"
#include "sigbus.h"
-#include "formats-util.h"
#include "signal-util.h"
-#include "journal-upload.h"
+#include "string-util.h"
+#include "util.h"
#define PRIV_KEY_FILE CERTIFICATE_ROOT "/private/journal-upload.pem"
#define CERT_FILE CERTIFICATE_ROOT "/certs/journal-upload.pem"
@@ -536,7 +542,7 @@ static int parse_config(void) {
{}};
return config_parse_many(PKGSYSCONFDIR "/journal-upload.conf",
- CONF_DIRS_NULSTR("systemd/journal-upload.conf"),
+ CONF_PATHS_NULSTR("systemd/journal-upload.conf.d"),
"Upload\0", config_item_table_lookup, items,
false, NULL);
}
@@ -619,9 +625,7 @@ static int parse_argv(int argc, char *argv[]) {
return 0 /* done */;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0 /* done */;
+ return version();
case 'u':
if (arg_url) {
diff --git a/src/journal-remote/journal-upload.h b/src/journal-remote/journal-upload.h
index 3b46fa8cbf..b8cd04d527 100644
--- a/src/journal-remote/journal-upload.h
+++ b/src/journal-remote/journal-upload.h
@@ -2,8 +2,8 @@
#include <inttypes.h>
-#include "sd-journal.h"
#include "sd-event.h"
+#include "sd-journal.h"
typedef enum {
ENTRY_CURSOR = 0, /* Nothing actually written yet. */
diff --git a/src/journal-remote/log-generator.py b/src/journal-remote/log-generator.py
index 9a8fb07c7f..fd6964e758 100755
--- a/src/journal-remote/log-generator.py
+++ b/src/journal-remote/log-generator.py
@@ -6,6 +6,8 @@ import argparse
PARSER = argparse.ArgumentParser()
PARSER.add_argument('n', type=int)
PARSER.add_argument('--dots', action='store_true')
+PARSER.add_argument('--data-size', type=int, default=4000)
+PARSER.add_argument('--data-type', choices={'random', 'simple'})
OPTIONS = PARSER.parse_args()
template = """\
@@ -38,10 +40,16 @@ facility = 6
src = open('/dev/urandom', 'rb')
bytes = 0
+counter = 0
for i in range(OPTIONS.n):
message = repr(src.read(2000))
- data = repr(src.read(4000))
+ if OPTIONS.data_type == 'random':
+ data = repr(src.read(OPTIONS.data_size))
+ else:
+ # keep the pattern non-repeating so we get a different blob every time
+ data = '{:0{}}'.format(counter, OPTIONS.data_size)
+ counter += 1
entry = template.format(m=m,
realtime_ts=realtime_ts,
diff --git a/src/journal-remote/microhttpd-util.c b/src/journal-remote/microhttpd-util.c
index 8a11fba044..09e6da0031 100644
--- a/src/journal-remote/microhttpd-util.c
+++ b/src/journal-remote/microhttpd-util.c
@@ -24,17 +24,19 @@
#include <stdio.h>
#include <string.h>
-#include "microhttpd-util.h"
-#include "log.h"
-#include "macro.h"
-#include "util.h"
-#include "strv.h"
-
#ifdef HAVE_GNUTLS
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
#endif
+#include "alloc-util.h"
+#include "log.h"
+#include "macro.h"
+#include "microhttpd-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "util.h"
+
void microhttpd_logger(void *arg, const char *fmt, va_list ap) {
char *f;
diff --git a/src/journal-remote/microhttpd-util.h b/src/journal-remote/microhttpd-util.h
index b2feb9180a..cba57403a3 100644
--- a/src/journal-remote/microhttpd-util.h
+++ b/src/journal-remote/microhttpd-util.h
@@ -21,11 +21,20 @@
#pragma once
-#include <stdarg.h>
#include <microhttpd.h>
+#include <stdarg.h>
#include "macro.h"
+/* Compatiblity with libmicrohttpd < 0.9.38 */
+#ifndef MHD_HTTP_NOT_ACCEPTABLE
+#define MHD_HTTP_NOT_ACCEPTABLE MHD_HTTP_METHOD_NOT_ACCEPTABLE
+#endif
+
+#if MHD_VERSION < 0x00094203
+#define MHD_create_response_from_fd_at_offset64 MHD_create_response_from_fd_at_offset
+#endif
+
void microhttpd_logger(void *arg, const char *fmt, va_list ap) _printf_(2, 0);
/* respond_oom() must be usable with return, hence this form. */
diff --git a/src/journal/audit-type.c b/src/journal/audit-type.c
index 4888c7d05d..086bf7e7e3 100644
--- a/src/journal/audit-type.c
+++ b/src/journal/audit-type.c
@@ -25,8 +25,7 @@
# include <libaudit.h>
#endif
-#include "audit-type.h"
-#include "macro.h"
#include "missing.h"
-
+#include "audit-type.h"
#include "audit_type-to-name.h"
+#include "macro.h"
diff --git a/src/journal/cat.c b/src/journal/cat.c
index be2c2e3354..7fd4198df8 100644
--- a/src/journal/cat.c
+++ b/src/journal/cat.c
@@ -19,17 +19,20 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
-#include <getopt.h>
-#include <unistd.h>
-#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
-#include "systemd/sd-journal.h"
+#include "sd-journal.h"
+#include "fd-util.h"
+#include "parse-util.h"
+#include "string-util.h"
+#include "syslog-util.h"
#include "util.h"
-#include "build.h"
static char *arg_identifier = NULL;
static int arg_priority = LOG_INFO;
@@ -76,9 +79,7 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case 't':
free(arg_identifier);
@@ -95,7 +96,7 @@ static int parse_argv(int argc, char *argv[]) {
arg_priority = log_level_from_string(optarg);
if (arg_priority < 0) {
log_error("Failed to parse priority value.");
- return arg_priority;
+ return -EINVAL;
}
break;
@@ -103,10 +104,9 @@ static int parse_argv(int argc, char *argv[]) {
int k;
k = parse_boolean(optarg);
- if (k < 0) {
- log_error("Failed to parse level prefix value.");
- return k;
- }
+ if (k < 0)
+ return log_error_errno(k, "Failed to parse level prefix value.");
+
arg_level_prefix = k;
break;
}
@@ -122,7 +122,8 @@ static int parse_argv(int argc, char *argv[]) {
}
int main(int argc, char *argv[]) {
- int r, fd = -1, saved_stderr = -1;
+ _cleanup_close_ int fd = -1, saved_stderr = -1;
+ int r;
log_parse_environment();
log_open();
@@ -133,8 +134,7 @@ int main(int argc, char *argv[]) {
fd = sd_journal_stream_fd(arg_identifier, arg_priority, arg_level_prefix);
if (fd < 0) {
- log_error_errno(fd, "Failed to create stream fd: %m");
- r = fd;
+ r = log_error_errno(fd, "Failed to create stream fd: %m");
goto finish;
}
@@ -148,25 +148,20 @@ int main(int argc, char *argv[]) {
if (fd >= 3)
safe_close(fd);
-
fd = -1;
if (argc <= optind)
- execl("/bin/cat", "/bin/cat", NULL);
+ (void) execl("/bin/cat", "/bin/cat", NULL);
else
- execvp(argv[optind], argv + optind);
-
+ (void) execvp(argv[optind], argv + optind);
r = -errno;
/* Let's try to restore a working stderr, so we can print the error message */
if (saved_stderr >= 0)
- dup3(saved_stderr, STDERR_FILENO, 0);
+ (void) dup3(saved_stderr, STDERR_FILENO, 0);
log_error_errno(r, "Failed to execute process: %m");
finish:
- safe_close(fd);
- safe_close(saved_stderr);
-
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/journal/catalog.c b/src/journal/catalog.c
index a3e51e2f52..fcaa54aa0c 100644
--- a/src/journal/catalog.c
+++ b/src/journal/catalog.c
@@ -19,25 +19,31 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
#include <fcntl.h>
+#include <locale.h>
#include <stdio.h>
-#include <unistd.h>
-#include <errno.h>
#include <string.h>
#include <sys/mman.h>
-#include <locale.h>
+#include <unistd.h>
-#include "util.h"
-#include "log.h"
-#include "sparse-endian.h"
#include "sd-id128.h"
-#include "hashmap.h"
-#include "strv.h"
-#include "strbuf.h"
+
+#include "alloc-util.h"
+#include "catalog.h"
#include "conf-files.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "hashmap.h"
+#include "log.h"
#include "mkdir.h"
-#include "catalog.h"
+#include "path-util.h"
#include "siphash24.h"
+#include "sparse-endian.h"
+#include "strbuf.h"
+#include "string-util.h"
+#include "strv.h"
+#include "util.h"
const char * const catalog_file_dirs[] = {
"/usr/local/lib/systemd/catalog/",
@@ -62,21 +68,11 @@ typedef struct CatalogItem {
le64_t offset;
} CatalogItem;
-static unsigned long catalog_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
+static void catalog_hash_func(const void *p, struct siphash *state) {
const CatalogItem *i = p;
- uint64_t u;
- size_t l, sz;
- void *v;
- l = strlen(i->language);
- sz = sizeof(i->id) + l;
- v = alloca(sz);
-
- memcpy(mempcpy(v, &i->id, sizeof(i->id)), i->language, l);
-
- siphash24((uint8_t*) &u, v, sz, hash_key);
-
- return (unsigned long) u;
+ siphash24_compress(&i->id, sizeof(i->id), state);
+ siphash24_compress(i->language, strlen(i->language), state);
}
static int catalog_compare_func(const void *a, const void *b) {
@@ -212,7 +208,7 @@ int catalog_import_file(Hashmap *h, struct strbuf *sb, const char *path) {
r = catalog_file_lang(path, &deflang);
if (r < 0)
- log_error_errno(errno, "Failed to determine language for file %s: %m", path);
+ log_error_errno(r, "Failed to determine language for file %s: %m", path);
if (r == 1)
log_debug("File %s has language %s.", path, deflang);
@@ -225,8 +221,7 @@ int catalog_import_file(Hashmap *h, struct strbuf *sb, const char *path) {
if (feof(f))
break;
- log_error_errno(errno, "Failed to read file %s: %m", path);
- return -errno;
+ return log_error_errno(errno, "Failed to read file %s: %m", path);
}
n++;
@@ -323,8 +318,8 @@ int catalog_import_file(Hashmap *h, struct strbuf *sb, const char *path) {
return 0;
}
-static long write_catalog(const char *database, Hashmap *h, struct strbuf *sb,
- CatalogItem *items, size_t n) {
+static int64_t write_catalog(const char *database, struct strbuf *sb,
+ CatalogItem *items, size_t n) {
CatalogHeader header;
_cleanup_fclose_ FILE *w = NULL;
int r;
@@ -348,7 +343,7 @@ static long write_catalog(const char *database, Hashmap *h, struct strbuf *sb,
memcpy(header.signature, CATALOG_SIGNATURE, sizeof(header.signature));
header.header_size = htole64(ALIGN_TO(sizeof(CatalogHeader), 8));
header.catalog_item_size = htole64(sizeof(CatalogItem));
- header.n_items = htole64(hashmap_size(h));
+ header.n_items = htole64(n);
r = -EIO;
@@ -383,7 +378,7 @@ static long write_catalog(const char *database, Hashmap *h, struct strbuf *sb,
goto error;
}
- return ftell(w);
+ return ftello(w);
error:
(void) unlink(p);
@@ -399,7 +394,8 @@ int catalog_update(const char* database, const char* root, const char* const* di
CatalogItem *i;
Iterator j;
unsigned n;
- long r;
+ int r;
+ int64_t sz;
h = hashmap_new(&catalog_hash_ops);
sb = strbuf_new();
@@ -419,8 +415,7 @@ int catalog_update(const char* database, const char* root, const char* const* di
log_debug("Reading file '%s'", *f);
r = catalog_import_file(h, sb, *f);
if (r < 0) {
- log_error("Failed to import file '%s': %s.",
- *f, strerror(-r));
+ log_error_errno(r, "Failed to import file '%s': %m", *f);
goto finish;
}
}
@@ -450,18 +445,19 @@ int catalog_update(const char* database, const char* root, const char* const* di
assert(n == hashmap_size(h));
qsort_safe(items, n, sizeof(CatalogItem), catalog_compare_func);
- r = write_catalog(database, h, sb, items, n);
- if (r < 0)
- log_error_errno(r, "Failed to write %s: %m", database);
- else
- log_debug("%s: wrote %u items, with %zu bytes of strings, %ld total size.",
- database, n, sb->len, r);
+ sz = write_catalog(database, sb, items, n);
+ if (sz < 0)
+ r = log_error_errno(sz, "Failed to write %s: %m", database);
+ else {
+ r = 0;
+ log_debug("%s: wrote %u items, with %zu bytes of strings, %"PRIi64" total size.",
+ database, n, sb->len, sz);
+ }
finish:
- if (sb)
- strbuf_cleanup(sb);
+ strbuf_cleanup(sb);
- return r < 0 ? r : 0;
+ return r;
}
static int open_mmap(const char *database, int *_fd, struct stat *_st, void **_p) {
@@ -676,8 +672,7 @@ int catalog_list_items(FILE *f, const char *database, bool oneline, char **items
k = sd_id128_from_string(*item, &id);
if (k < 0) {
- log_error_errno(k, "Failed to parse id128 '%s': %m",
- *item);
+ log_error_errno(k, "Failed to parse id128 '%s': %m", *item);
if (r == 0)
r = k;
continue;
@@ -685,9 +680,8 @@ int catalog_list_items(FILE *f, const char *database, bool oneline, char **items
k = catalog_get(database, id, &msg);
if (k < 0) {
- log_full(k == -ENOENT ? LOG_NOTICE : LOG_ERR,
- "Failed to retrieve catalog entry for '%s': %s",
- *item, strerror(-k));
+ log_full_errno(k == -ENOENT ? LOG_NOTICE : LOG_ERR, k,
+ "Failed to retrieve catalog entry for '%s': %m", *item);
if (r == 0)
r = k;
continue;
diff --git a/src/journal/catalog.h b/src/journal/catalog.h
index a72ecf6de7..bcc73c2631 100644
--- a/src/journal/catalog.h
+++ b/src/journal/catalog.h
@@ -24,6 +24,7 @@
#include <stdbool.h>
#include "sd-id128.h"
+
#include "hashmap.h"
#include "strbuf.h"
diff --git a/src/journal/compress.c b/src/journal/compress.c
index c66043e503..78935fee74 100644
--- a/src/journal/compress.c
+++ b/src/journal/compress.c
@@ -21,21 +21,33 @@
#include <stdlib.h>
#include <string.h>
+#include <sys/mman.h>
#include <unistd.h>
#ifdef HAVE_XZ
-# include <lzma.h>
+#include <lzma.h>
#endif
#ifdef HAVE_LZ4
-# include <lz4.h>
+#include <lz4.h>
+#include <lz4frame.h>
#endif
+#include "alloc-util.h"
#include "compress.h"
+#include "fd-util.h"
+#include "io-util.h"
+#include "journal-def.h"
#include "macro.h"
-#include "util.h"
#include "sparse-endian.h"
-#include "journal-def.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "util.h"
+
+#ifdef HAVE_LZ4
+DEFINE_TRIVIAL_CLEANUP_FUNC(LZ4F_compressionContext_t, LZ4F_freeCompressionContext);
+DEFINE_TRIVIAL_CLEANUP_FUNC(LZ4F_decompressionContext_t, LZ4F_freeDecompressionContext);
+#endif
#define ALIGN_8(l) ALIGN_TO(l, sizeof(size_t))
@@ -46,14 +58,16 @@ static const char* const object_compressed_table[_OBJECT_COMPRESSED_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(object_compressed, int);
-int compress_blob_xz(const void *src, uint64_t src_size, void *dst, size_t *dst_size) {
+int compress_blob_xz(const void *src, uint64_t src_size,
+ void *dst, size_t dst_alloc_size, size_t *dst_size) {
#ifdef HAVE_XZ
static const lzma_options_lzma opt = {
1u << 20u, NULL, 0, LZMA_LC_DEFAULT, LZMA_LP_DEFAULT,
- LZMA_PB_DEFAULT, LZMA_MODE_FAST, 128, LZMA_MF_HC3, 4};
- static const lzma_filter filters[2] = {
- {LZMA_FILTER_LZMA2, (lzma_options_lzma*) &opt},
- {LZMA_VLI_UNKNOWN, NULL}
+ LZMA_PB_DEFAULT, LZMA_MODE_FAST, 128, LZMA_MF_HC3, 4
+ };
+ static const lzma_filter filters[] = {
+ { LZMA_FILTER_LZMA2, (lzma_options_lzma*) &opt },
+ { LZMA_VLI_UNKNOWN, NULL }
};
lzma_ret ret;
size_t out_pos = 0;
@@ -61,6 +75,7 @@ int compress_blob_xz(const void *src, uint64_t src_size, void *dst, size_t *dst_
assert(src);
assert(src_size > 0);
assert(dst);
+ assert(dst_alloc_size > 0);
assert(dst_size);
/* Returns < 0 if we couldn't compress the data or the
@@ -70,7 +85,7 @@ int compress_blob_xz(const void *src, uint64_t src_size, void *dst, size_t *dst_
return -ENOBUFS;
ret = lzma_stream_buffer_encode((lzma_filter*) filters, LZMA_CHECK_NONE, NULL,
- src, src_size, dst, &out_pos, src_size - 1);
+ src, src_size, dst, &out_pos, dst_alloc_size);
if (ret != LZMA_OK)
return -ENOBUFS;
@@ -81,13 +96,15 @@ int compress_blob_xz(const void *src, uint64_t src_size, void *dst, size_t *dst_
#endif
}
-int compress_blob_lz4(const void *src, uint64_t src_size, void *dst, size_t *dst_size) {
+int compress_blob_lz4(const void *src, uint64_t src_size,
+ void *dst, size_t dst_alloc_size, size_t *dst_size) {
#ifdef HAVE_LZ4
int r;
assert(src);
assert(src_size > 0);
assert(dst);
+ assert(dst_alloc_size > 0);
assert(dst_size);
/* Returns < 0 if we couldn't compress the data or the
@@ -96,7 +113,7 @@ int compress_blob_lz4(const void *src, uint64_t src_size, void *dst, size_t *dst
if (src_size < 9)
return -ENOBUFS;
- r = LZ4_compress_limitedOutput(src, dst + 8, src_size, src_size - 8 - 1);
+ r = LZ4_compress_limitedOutput(src, dst + 8, src_size, (int) dst_alloc_size - 8);
if (r <= 0)
return -ENOBUFS;
@@ -188,7 +205,7 @@ int decompress_blob_lz4(const void *src, uint64_t src_size,
return -EBADMSG;
size = le64toh( *(le64_t*)src );
- if (size < 0 || (le64_t) size != *(le64_t*)src)
+ if (size < 0 || (unsigned) size != le64toh(*(le64_t*)src))
return -EFBIG;
if ((size_t) size > *dst_alloc_size) {
out = realloc(*dst, size);
@@ -293,6 +310,7 @@ int decompress_startswith_lz4(const void *src, uint64_t src_size,
* prefix */
int r;
+ size_t size;
assert(src);
assert(src_size > 0);
@@ -309,10 +327,18 @@ int decompress_startswith_lz4(const void *src, uint64_t src_size,
r = LZ4_decompress_safe_partial(src + 8, *buffer, src_size - 8,
prefix_len + 1, *buffer_size);
+ if (r >= 0)
+ size = (unsigned) r;
+ else {
+ /* lz4 always tries to decode full "sequence", so in
+ * pathological cases might need to decompress the
+ * full field. */
+ r = decompress_blob_lz4(src, src_size, buffer, buffer_size, &size, 0);
+ if (r < 0)
+ return r;
+ }
- if (r < 0)
- return -EBADMSG;
- if ((unsigned) r >= prefix_len + 1)
+ if (size >= prefix_len + 1)
return memcmp(*buffer, prefix, prefix_len) == 0 &&
((const uint8_t*) *buffer)[prefix_len] == extra;
else
@@ -416,81 +442,96 @@ int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes) {
#endif
}
-#define LZ4_BUFSIZE (512*1024)
+#define LZ4_BUFSIZE (512*1024u)
int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes) {
#ifdef HAVE_LZ4
+ LZ4F_errorCode_t c;
+ _cleanup_(LZ4F_freeCompressionContextp) LZ4F_compressionContext_t ctx = NULL;
+ _cleanup_free_ char *buf = NULL;
+ char *src = NULL;
+ size_t size, n, total_in = 0, total_out, offset = 0, frame_size;
+ struct stat st;
+ int r;
+ static const LZ4F_compressOptions_t options = {
+ .stableSrc = 1,
+ };
+ static const LZ4F_preferences_t preferences = {
+ .frameInfo.blockSizeID = 5,
+ };
- _cleanup_free_ char *buf1 = NULL, *buf2 = NULL, *out = NULL;
- char *buf;
- LZ4_stream_t lz4_data = {};
- le32_t header;
- size_t total_in = 0, total_out = sizeof(header);
- ssize_t n;
+ c = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
+ if (LZ4F_isError(c))
+ return -ENOMEM;
- assert(fdf >= 0);
- assert(fdt >= 0);
+ if (fstat(fdf, &st) < 0)
+ return log_debug_errno(errno, "fstat() failed: %m");
- buf1 = malloc(LZ4_BUFSIZE);
- buf2 = malloc(LZ4_BUFSIZE);
- out = malloc(LZ4_COMPRESSBOUND(LZ4_BUFSIZE));
- if (!buf1 || !buf2 || !out)
- return log_oom();
+ frame_size = LZ4F_compressBound(LZ4_BUFSIZE, &preferences);
+ size = frame_size + 64*1024; /* add some space for header and trailer */
+ buf = malloc(size);
+ if (!buf)
+ return -ENOMEM;
- buf = buf1;
- for (;;) {
- size_t m;
- int r;
+ n = offset = total_out = LZ4F_compressBegin(ctx, buf, size, &preferences);
+ if (LZ4F_isError(n))
+ return -EINVAL;
- m = LZ4_BUFSIZE;
- if (max_bytes != (uint64_t) -1 && (uint64_t) m > (max_bytes - total_in))
- m = (size_t) (max_bytes - total_in);
+ src = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fdf, 0);
+ if (src == MAP_FAILED)
+ return -errno;
- n = read(fdf, buf, m);
- if (n < 0)
- return -errno;
- if (n == 0)
- break;
+ log_debug("Buffer size is %zu bytes, header size %zu bytes.", size, n);
- total_in += n;
+ while (total_in < (size_t) st.st_size) {
+ ssize_t k;
- r = LZ4_compress_continue(&lz4_data, buf, out, n);
- if (r == 0) {
- log_error("LZ4 compression failed.");
- return -EBADMSG;
+ k = MIN(LZ4_BUFSIZE, st.st_size - total_in);
+ n = LZ4F_compressUpdate(ctx, buf + offset, size - offset,
+ src + total_in, k, &options);
+ if (LZ4F_isError(n)) {
+ r = -ENOTRECOVERABLE;
+ goto cleanup;
}
- header = htole32(r);
- errno = 0;
-
- n = write(fdt, &header, sizeof(header));
- if (n < 0)
- return -errno;
- if (n != sizeof(header))
- return errno ? -errno : -EIO;
+ total_in += k;
+ offset += n;
+ total_out += n;
- n = loop_write(fdt, out, r, false);
- if (n < 0)
- return n;
+ if (max_bytes != (uint64_t) -1 && total_out > (size_t) max_bytes) {
+ log_debug("Compressed stream longer than %zd bytes", max_bytes);
+ return -EFBIG;
+ }
- total_out += sizeof(header) + r;
+ if (size - offset < frame_size + 4) {
+ k = loop_write(fdt, buf, offset, false);
+ if (k < 0) {
+ r = k;
+ goto cleanup;
+ }
+ offset = 0;
+ }
+ }
- buf = buf == buf1 ? buf2 : buf1;
+ n = LZ4F_compressEnd(ctx, buf + offset, size - offset, &options);
+ if (LZ4F_isError(n)) {
+ r = -ENOTRECOVERABLE;
+ goto cleanup;
}
- header = htole32(0);
- n = write(fdt, &header, sizeof(header));
- if (n < 0)
- return -errno;
- if (n != sizeof(header))
- return errno ? -errno : -EIO;
+ offset += n;
+ total_out += n;
+ r = loop_write(fdt, buf, offset, false);
+ if (r < 0)
+ goto cleanup;
log_debug("LZ4 compression finished (%zu -> %zu bytes, %.1f%%)",
total_in, total_out,
(double) total_out / total_in * 100);
-
- return 0;
+ cleanup:
+ munmap(src, st.st_size);
+ return r;
#else
return -EPROTONOSUPPORT;
#endif
@@ -510,7 +551,7 @@ int decompress_stream_xz(int fdf, int fdt, uint64_t max_bytes) {
ret = lzma_stream_decoder(&s, UINT64_MAX, 0);
if (ret != LZMA_OK) {
- log_error("Failed to initialize XZ decoder: code %u", ret);
+ log_debug("Failed to initialize XZ decoder: code %u", ret);
return -ENOMEM;
}
@@ -536,7 +577,7 @@ int decompress_stream_xz(int fdf, int fdt, uint64_t max_bytes) {
ret = lzma_code(&s, action);
if (ret != LZMA_OK && ret != LZMA_STREAM_END) {
- log_error("Decompression failed: code %u", ret);
+ log_debug("Decompression failed: code %u", ret);
return -EBADMSG;
}
@@ -566,82 +607,68 @@ int decompress_stream_xz(int fdf, int fdt, uint64_t max_bytes) {
}
}
#else
- log_error("Cannot decompress file. Compiled without XZ support.");
+ log_debug("Cannot decompress file. Compiled without XZ support.");
return -EPROTONOSUPPORT;
#endif
}
-int decompress_stream_lz4(int fdf, int fdt, uint64_t max_bytes) {
-
+int decompress_stream_lz4(int in, int out, uint64_t max_bytes) {
#ifdef HAVE_LZ4
- _cleanup_free_ char *buf = NULL, *out = NULL;
- size_t buf_size = 0;
- LZ4_streamDecode_t lz4_data = {};
- le32_t header;
- size_t total_in = sizeof(header), total_out = 0;
-
- assert(fdf >= 0);
- assert(fdt >= 0);
+ size_t c;
+ _cleanup_(LZ4F_freeDecompressionContextp) LZ4F_decompressionContext_t ctx = NULL;
+ _cleanup_free_ char *buf = NULL;
+ char *src;
+ struct stat st;
+ int r = 0;
+ size_t total_in = 0, total_out = 0;
+
+ c = LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION);
+ if (LZ4F_isError(c))
+ return -ENOMEM;
- out = malloc(4*LZ4_BUFSIZE);
- if (!out)
- return log_oom();
+ if (fstat(in, &st) < 0)
+ return log_debug_errno(errno, "fstat() failed: %m");
- for (;;) {
- ssize_t m;
- int r;
+ buf = malloc(LZ4_BUFSIZE);
+ if (!buf)
+ return -ENOMEM;
- r = loop_read_exact(fdf, &header, sizeof(header), false);
- if (r < 0)
- return r;
+ src = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, in, 0);
+ if (src == MAP_FAILED)
+ return -errno;
- m = le32toh(header);
- if (m == 0)
- break;
+ while (total_in < (size_t) st.st_size) {
+ size_t produced = LZ4_BUFSIZE;
+ size_t used = st.st_size - total_in;
- /* We refuse to use a bigger decompression buffer than
- * the one used for compression by 4 times. This means
- * that compression buffer size can be enlarged 4
- * times. This can be changed, but old binaries might
- * not accept buffers compressed by newer binaries then.
- */
- if (m > LZ4_COMPRESSBOUND(LZ4_BUFSIZE * 4)) {
- log_error("Compressed stream block too big: %zd bytes", m);
- return -EBADMSG;
+ c = LZ4F_decompress(ctx, buf, &produced, src + total_in, &used, NULL);
+ if (LZ4F_isError(c)) {
+ r = -EBADMSG;
+ goto cleanup;
}
- total_in += sizeof(header) + m;
-
- if (!GREEDY_REALLOC(buf, buf_size, m))
- return log_oom();
-
- 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)
- log_error("LZ4 decompression failed.");
+ total_in += used;
+ total_out += produced;
- total_out += r;
-
- if (max_bytes != (uint64_t) -1 && (uint64_t) total_out > max_bytes) {
- log_debug("Decompressed stream longer than %" PRIu64 " bytes", max_bytes);
- return -EFBIG;
+ if (max_bytes != (uint64_t) -1 && total_out > (size_t) max_bytes) {
+ log_debug("Decompressed stream longer than %zd bytes", max_bytes);
+ r = -EFBIG;
+ goto cleanup;
}
- r = loop_write(fdt, out, r, false);
+ r = loop_write(out, buf, produced, false);
if (r < 0)
- return r;
+ goto cleanup;
}
log_debug("LZ4 decompression finished (%zu -> %zu bytes, %.1f%%)",
total_in, total_out,
(double) total_out / total_in * 100);
-
- return 0;
+ cleanup:
+ munmap(src, st.st_size);
+ return r;
#else
- log_error("Cannot decompress file. Compiled without LZ4 support.");
+ log_debug("Cannot decompress file. Compiled without LZ4 support.");
return -EPROTONOSUPPORT;
#endif
}
diff --git a/src/journal/compress.h b/src/journal/compress.h
index 9a065eb763..758598730a 100644
--- a/src/journal/compress.h
+++ b/src/journal/compress.h
@@ -28,17 +28,20 @@
const char* object_compressed_to_string(int compression);
int object_compressed_from_string(const char *compression);
-int compress_blob_xz(const void *src, uint64_t src_size, void *dst, size_t *dst_size);
-int compress_blob_lz4(const void *src, uint64_t src_size, void *dst, size_t *dst_size);
+int compress_blob_xz(const void *src, uint64_t src_size,
+ void *dst, size_t dst_alloc_size, size_t *dst_size);
+int compress_blob_lz4(const void *src, uint64_t src_size,
+ void *dst, size_t dst_alloc_size, size_t *dst_size);
-static inline int compress_blob(const void *src, uint64_t src_size, void *dst, size_t *dst_size) {
+static inline int compress_blob(const void *src, uint64_t src_size,
+ void *dst, size_t dst_alloc_size, size_t *dst_size) {
int r;
#ifdef HAVE_LZ4
- r = compress_blob_lz4(src, src_size, dst, dst_size);
+ r = compress_blob_lz4(src, src_size, dst, dst_alloc_size, dst_size);
if (r == 0)
return OBJECT_COMPRESSED_LZ4;
#else
- r = compress_blob_xz(src, src_size, dst, dst_size);
+ r = compress_blob_xz(src, src_size, dst, dst_alloc_size, dst_size);
if (r == 0)
return OBJECT_COMPRESSED_XZ;
#endif
diff --git a/src/journal/coredump-vacuum.c b/src/journal/coredump-vacuum.c
index efe418615a..09ab60c6c4 100644
--- a/src/journal/coredump-vacuum.c
+++ b/src/journal/coredump-vacuum.c
@@ -21,12 +21,16 @@
#include <sys/statvfs.h>
-#include "util.h"
-#include "time-util.h"
+#include "alloc-util.h"
+#include "coredump-vacuum.h"
+#include "dirent-util.h"
+#include "fd-util.h"
#include "hashmap.h"
#include "macro.h"
-
-#include "coredump-vacuum.h"
+#include "string-util.h"
+#include "time-util.h"
+#include "user-util.h"
+#include "util.h"
#define DEFAULT_MAX_USE_LOWER (uint64_t) (1ULL*1024ULL*1024ULL) /* 1 MiB */
#define DEFAULT_MAX_USE_UPPER (uint64_t) (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */
@@ -153,8 +157,7 @@ int coredump_vacuum(int exclude_fd, uint64_t keep_free, uint64_t max_use) {
if (errno == ENOENT)
return 0;
- log_error_errno(errno, "Can't open coredump directory: %m");
- return -errno;
+ return log_error_errno(errno, "Can't open coredump directory: %m");
}
for (;;) {
@@ -179,7 +182,7 @@ int coredump_vacuum(int exclude_fd, uint64_t keep_free, uint64_t max_use) {
if (errno == ENOENT)
continue;
- log_warning("Failed to stat /var/lib/systemd/coredump/%s", de->d_name);
+ log_warning_errno(errno, "Failed to stat /var/lib/systemd/coredump/%s: %m", de->d_name);
continue;
}
@@ -197,7 +200,7 @@ int coredump_vacuum(int exclude_fd, uint64_t keep_free, uint64_t max_use) {
t = timespec_load(&st.st_mtim);
- c = hashmap_get(h, UINT32_TO_PTR(uid));
+ c = hashmap_get(h, UID_TO_PTR(uid));
if (c) {
if (t < c->oldest_mtime) {
@@ -225,7 +228,7 @@ int coredump_vacuum(int exclude_fd, uint64_t keep_free, uint64_t max_use) {
n->oldest_mtime = t;
- r = hashmap_put(h, UINT32_TO_PTR(uid), n);
+ r = hashmap_put(h, UID_TO_PTR(uid), n);
if (r < 0)
return log_oom();
@@ -255,8 +258,7 @@ int coredump_vacuum(int exclude_fd, uint64_t keep_free, uint64_t max_use) {
if (errno == ENOENT)
continue;
- log_error_errno(errno, "Failed to remove file %s: %m", worst->oldest_file);
- return -errno;
+ return log_error_errno(errno, "Failed to remove file %s: %m", worst->oldest_file);
} else
log_info("Removed old coredump %s.", worst->oldest_file);
}
@@ -264,6 +266,5 @@ int coredump_vacuum(int exclude_fd, uint64_t keep_free, uint64_t max_use) {
return 0;
fail:
- log_error_errno(errno, "Failed to read directory: %m");
- return -errno;
+ return log_error_errno(errno, "Failed to read directory: %m");
}
diff --git a/src/journal/coredump.c b/src/journal/coredump.c
index e1e66b9826..869c8fea03 100644
--- a/src/journal/coredump.c
+++ b/src/journal/coredump.c
@@ -20,10 +20,10 @@
***/
#include <errno.h>
-#include <unistd.h>
#include <stdio.h>
#include <sys/prctl.h>
#include <sys/xattr.h>
+#include <unistd.h>
#ifdef HAVE_ELFUTILS
# include <dwarf.h>
@@ -32,23 +32,34 @@
#include "sd-journal.h"
#include "sd-login.h"
-#include "log.h"
-#include "util.h"
-#include "fileio.h"
-#include "strv.h"
-#include "macro.h"
-#include "mkdir.h"
-#include "special.h"
+
+#include "acl-util.h"
+#include "alloc-util.h"
+#include "capability-util.h"
#include "cgroup-util.h"
+#include "compress.h"
#include "conf-parser.h"
#include "copy.h"
-#include "stacktrace.h"
-#include "compress.h"
-#include "acl-util.h"
-#include "capability.h"
-#include "journald-native.h"
#include "coredump-vacuum.h"
+#include "dirent-util.h"
+#include "escape.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "io-util.h"
+#include "journald-native.h"
+#include "log.h"
+#include "macro.h"
+#include "mkdir.h"
+#include "parse-util.h"
#include "process-util.h"
+#include "special.h"
+#include "stacktrace.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "strv.h"
+#include "user-util.h"
+#include "util.h"
/* The maximum size up to which we process coredumps */
#define PROCESS_SIZE_MAX ((uint64_t) (2LLU*1024LLU*1024LLU*1024LLU))
@@ -115,8 +126,8 @@ static int parse_config(void) {
{}
};
- return config_parse_many("/etc/systemd/coredump.conf",
- CONF_DIRS_NULSTR("systemd/coredump.conf"),
+ return config_parse_many(PKGSYSCONFDIR "/coredump.conf",
+ CONF_PATHS_NULSTR("systemd/coredump.conf.d"),
"Coredump\0",
config_item_table_lookup, items,
false, NULL);
@@ -128,6 +139,7 @@ static int fix_acl(int fd, uid_t uid) {
_cleanup_(acl_freep) acl_t acl = NULL;
acl_entry_t entry;
acl_permset_t permset;
+ int r;
assert(fd >= 0);
@@ -149,11 +161,12 @@ static int fix_acl(int fd, uid_t uid) {
}
if (acl_get_permset(entry, &permset) < 0 ||
- acl_add_perm(permset, ACL_READ) < 0 ||
- calc_acl_mask_if_needed(&acl) < 0) {
- log_warning_errno(errno, "Failed to patch ACL: %m");
- return -errno;
- }
+ acl_add_perm(permset, ACL_READ) < 0)
+ return log_warning_errno(errno, "Failed to patch ACL: %m");
+
+ r = calc_acl_mask_if_needed(&acl);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to patch ACL: %m");
if (acl_set_fd(fd, acl) < 0)
return log_error_errno(errno, "Failed to apply ACL: %m");
@@ -514,7 +527,7 @@ static int compose_open_fds(pid_t pid, char **open_fds) {
errno = 0;
stream = safe_fclose(stream);
- if (errno != 0)
+ if (errno > 0)
return -errno;
*open_fds = buffer;
diff --git a/src/journal/coredumpctl.c b/src/journal/coredumpctl.c
index 6628e82421..40ffa6afbe 100644
--- a/src/journal/coredumpctl.c
+++ b/src/journal/coredumpctl.c
@@ -19,27 +19,33 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <fcntl.h>
+#include <getopt.h>
#include <locale.h>
#include <stdio.h>
#include <string.h>
-#include <getopt.h>
-#include <fcntl.h>
#include <unistd.h>
#include "sd-journal.h"
-#include "build.h"
-#include "set.h"
-#include "util.h"
+
+#include "alloc-util.h"
+#include "compress.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "journal-internal.h"
#include "log.h"
-#include "path-util.h"
-#include "pager.h"
#include "macro.h"
-#include "journal-internal.h"
-#include "compress.h"
-#include "sigbus.h"
+#include "pager.h"
+#include "parse-util.h"
+#include "path-util.h"
#include "process-util.h"
-#include "terminal-util.h"
+#include "set.h"
+#include "sigbus.h"
#include "signal-util.h"
+#include "string-util.h"
+#include "terminal-util.h"
+#include "user-util.h"
+#include "util.h"
static enum {
ACTION_NONE,
@@ -84,37 +90,35 @@ static Set *new_matches(void) {
}
static int add_match(Set *set, const char *match) {
- int r = -ENOMEM;
- unsigned pid;
- const char* prefix;
- char *pattern = NULL;
_cleanup_free_ char *p = NULL;
+ char *pattern = NULL;
+ const char* prefix;
+ pid_t pid;
+ int r;
if (strchr(match, '='))
prefix = "";
else if (strchr(match, '/')) {
- p = path_make_absolute_cwd(match);
- if (!p)
+ r = path_make_absolute_cwd(match, &p);
+ if (r < 0)
goto fail;
-
match = p;
prefix = "COREDUMP_EXE=";
- }
- else if (safe_atou(match, &pid) == 0)
+ } else if (parse_pid(match, &pid) >= 0)
prefix = "COREDUMP_PID=";
else
prefix = "COREDUMP_COMM=";
pattern = strjoin(prefix, match, NULL);
- if (!pattern)
+ if (!pattern) {
+ r = -ENOMEM;
goto fail;
+ }
log_debug("Adding pattern: %s", pattern);
r = set_consume(set, pattern);
- if (r < 0) {
- log_error_errno(r, "Failed to add pattern: %m");
+ if (r < 0)
goto fail;
- }
return 0;
fail:
@@ -175,9 +179,7 @@ static int parse_argv(int argc, char *argv[], Set *matches) {
case ARG_VERSION:
arg_action = ACTION_NONE;
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case ARG_NO_PAGER:
arg_no_pager = true;
@@ -615,7 +617,7 @@ static int save_core(sd_journal *j, int fd, char **path, bool *unlink_temp) {
fdt = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC);
if (fdt < 0)
- return log_error_errno(errno, "Failed to create temporary file: %m");
+ return log_error_errno(fdt, "Failed to create temporary file: %m");
log_debug("Created temporary file %s", temp);
fd = fdt;
@@ -774,7 +776,7 @@ static int run_gdb(sd_journal *j) {
r = wait_for_terminate(pid, &st);
if (r < 0) {
- log_error_errno(errno, "Failed to wait for gdb: %m");
+ log_error_errno(r, "Failed to wait for gdb: %m");
goto finish;
}
@@ -790,7 +792,7 @@ finish:
}
int main(int argc, char *argv[]) {
- _cleanup_journal_close_ sd_journal*j = NULL;
+ _cleanup_(sd_journal_closep) sd_journal*j = NULL;
const char* match;
Iterator it;
int r = 0;
diff --git a/src/journal/fsprg.h b/src/journal/fsprg.h
index 150d034828..b79221fc2e 100644
--- a/src/journal/fsprg.h
+++ b/src/journal/fsprg.h
@@ -25,10 +25,11 @@
*
*/
-#include <sys/types.h>
#include <inttypes.h>
+#include <sys/types.h>
#include "macro.h"
+#include "util.h"
#ifdef __cplusplus
extern "C" {
diff --git a/src/journal/journal-authenticate.c b/src/journal/journal-authenticate.c
index cdc80e2d26..aeec83da1e 100644
--- a/src/journal/journal-authenticate.c
+++ b/src/journal/journal-authenticate.c
@@ -22,10 +22,12 @@
#include <fcntl.h>
#include <sys/mman.h>
+#include "fd-util.h"
+#include "fsprg.h"
+#include "hexdecoct.h"
+#include "journal-authenticate.h"
#include "journal-def.h"
#include "journal-file.h"
-#include "journal-authenticate.h"
-#include "fsprg.h"
static uint64_t journal_file_tag_seqnum(JournalFile *f) {
uint64_t r;
diff --git a/src/journal/journal-def.h b/src/journal/journal-def.h
index 39c9dd0dbf..c003ac05dd 100644
--- a/src/journal/journal-def.h
+++ b/src/journal/journal-def.h
@@ -21,11 +21,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "sparse-endian.h"
-
-#include "systemd/sd-id128.h"
+#include "sd-id128.h"
#include "macro.h"
+#include "sparse-endian.h"
/*
* If you change this file you probably should also change its documentation:
diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c
index 73d3a4bb9d..9e362bacae 100644
--- a/src/journal/journal-file.c
+++ b/src/journal/journal-file.c
@@ -19,22 +19,28 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/mman.h>
#include <errno.h>
-#include <sys/uio.h>
-#include <unistd.h>
-#include <sys/statvfs.h>
#include <fcntl.h>
-#include <stddef.h>
#include <linux/fs.h>
+#include <stddef.h>
+#include <sys/mman.h>
+#include <sys/statvfs.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include "alloc-util.h"
#include "btrfs-util.h"
+#include "chattr-util.h"
+#include "compress.h"
+#include "fd-util.h"
+#include "journal-authenticate.h"
#include "journal-def.h"
#include "journal-file.h"
-#include "journal-authenticate.h"
#include "lookup3.h"
-#include "compress.h"
+#include "parse-util.h"
#include "random-util.h"
+#include "string-util.h"
+#include "xattr-util.h"
#define DEFAULT_DATA_HASH_TABLE_SIZE (2047ULL*sizeof(HashItem))
#define DEFAULT_FIELD_HASH_TABLE_SIZE (333ULL*sizeof(HashItem))
@@ -42,13 +48,16 @@
#define COMPRESSION_SIZE_THRESHOLD (512ULL)
/* This is the minimum journal file size */
-#define JOURNAL_FILE_SIZE_MIN (4ULL*1024ULL*1024ULL) /* 4 MiB */
+#define JOURNAL_FILE_SIZE_MIN (512ULL*1024ULL) /* 512 KiB */
/* These are the lower and upper bounds if we deduce the max_use value
* from the file system size */
#define DEFAULT_MAX_USE_LOWER (1ULL*1024ULL*1024ULL) /* 1 MiB */
#define DEFAULT_MAX_USE_UPPER (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */
+/* This is the default minimal use limit, how much we'll use even if keep_free suggests otherwise. */
+#define DEFAULT_MIN_USE (1ULL*1024ULL*1024ULL) /* 1 MiB */
+
/* This is the upper bound if we deduce max_size from max_use */
#define DEFAULT_MAX_SIZE_UPPER (128ULL*1024ULL*1024ULL) /* 128 MiB */
@@ -60,6 +69,9 @@
* size */
#define DEFAULT_KEEP_FREE (1024ULL*1024ULL) /* 1 MB */
+/* This is the default maximum number of journal files to keep around. */
+#define DEFAULT_N_MAX_FILES (100)
+
/* n_data was the first entry we added after the initial file format design */
#define HEADER_SIZE_MIN ALIGN64(offsetof(Header, n_data))
@@ -128,7 +140,7 @@ int journal_file_set_offline(JournalFile *f) {
return 0;
}
-void journal_file_close(JournalFile *f) {
+JournalFile* journal_file_close(JournalFile *f) {
assert(f);
#ifdef HAVE_GCRYPT
@@ -157,8 +169,7 @@ void journal_file_close(JournalFile *f) {
safe_close(f->fd);
free(f->path);
- if (f->mmap)
- mmap_cache_unref(f->mmap);
+ mmap_cache_unref(f->mmap);
ordered_hashmap_free_free(f->chain_cache);
@@ -179,6 +190,7 @@ void journal_file_close(JournalFile *f) {
#endif
free(f);
+ return NULL;
}
static int journal_file_init_header(JournalFile *f, JournalFile *template) {
@@ -398,12 +410,7 @@ static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size)
if (fstatvfs(f->fd, &svfs) >= 0) {
uint64_t available;
- available = svfs.f_bfree * svfs.f_bsize;
-
- if (available >= f->metrics.keep_free)
- available -= f->metrics.keep_free;
- else
- available = 0;
+ available = LESS_BY((uint64_t) svfs.f_bfree * (uint64_t) svfs.f_bsize, f->metrics.keep_free);
if (new_size - old_size > available)
return -E2BIG;
@@ -604,10 +611,10 @@ static int journal_file_setup_data_hash_table(JournalFile *f) {
assert(f);
- /* We estimate that we need 1 hash table entry per 768 of
- journal file and we want to make sure we never get beyond
- 75% fill level. Calculate the hash table size for the
- maximum file size based on these metrics. */
+ /* We estimate that we need 1 hash table entry per 768 bytes
+ of journal file and we want to make sure we never get
+ beyond 75% fill level. Calculate the hash table size for
+ the maximum file size based on these metrics. */
s = (f->metrics.max_size * 4 / 768 / 3) * sizeof(HashItem);
if (s < DEFAULT_DATA_HASH_TABLE_SIZE)
@@ -1055,7 +1062,7 @@ static int journal_file_append_data(
r = journal_file_find_data_object_with_hash(f, data, size, hash, &o, &p);
if (r < 0)
return r;
- else if (r > 0) {
+ if (r > 0) {
if (ret)
*ret = o;
@@ -1074,23 +1081,24 @@ static int journal_file_append_data(
o->data.hash = htole64(hash);
#if defined(HAVE_XZ) || defined(HAVE_LZ4)
- if (f->compress_xz &&
- size >= COMPRESSION_SIZE_THRESHOLD) {
+ if (JOURNAL_FILE_COMPRESS(f) && size >= COMPRESSION_SIZE_THRESHOLD) {
size_t rsize = 0;
- compression = compress_blob(data, size, o->data.payload, &rsize);
+ compression = compress_blob(data, size, o->data.payload, size - 1, &rsize);
- if (compression) {
+ if (compression >= 0) {
o->object.size = htole64(offsetof(Object, data.payload) + rsize);
o->object.flags |= compression;
log_debug("Compressed data object %"PRIu64" -> %zu using %s",
size, rsize, object_compressed_to_string(compression));
- }
+ } else
+ /* Compression didn't work, we don't really care why, let's continue without compression */
+ compression = 0;
}
#endif
- if (!compression && size > 0)
+ if (compression == 0 && size > 0)
memcpy(o->data.payload, data, size);
r = journal_file_link_data(f, o, p, hash);
@@ -2696,7 +2704,7 @@ int journal_file_open(
}
if (f->last_stat.st_size < (off_t) HEADER_SIZE_MIN) {
- r = -EIO;
+ r = -ENODATA;
goto fail;
}
@@ -2833,8 +2841,7 @@ int journal_file_open_reliably(
size_t l;
_cleanup_free_ char *p = NULL;
- r = journal_file_open(fname, flags, mode, compress, seal,
- metrics, mmap_cache, template, ret);
+ r = journal_file_open(fname, flags, mode, compress, seal, metrics, mmap_cache, template, ret);
if (!IN_SET(r,
-EBADMSG, /* corrupted */
-ENODATA, /* truncated */
@@ -2864,8 +2871,7 @@ int journal_file_open_reliably(
random_u64()) < 0)
return -ENOMEM;
- r = rename(fname, p);
- if (r < 0)
+ if (rename(fname, p) < 0)
return -errno;
/* btrfs doesn't cope well with our write pattern and
@@ -2874,10 +2880,9 @@ int journal_file_open_reliably(
(void) chattr_path(p, false, FS_NOCOW_FL);
(void) btrfs_defrag(p);
- log_warning("File %s corrupted or uncleanly shut down, renaming and replacing.", fname);
+ log_warning_errno(r, "File %s corrupted or uncleanly shut down, renaming and replacing.", fname);
- return journal_file_open(fname, flags, mode, compress, seal,
- metrics, mmap_cache, template, ret);
+ return journal_file_open(fname, flags, mode, compress, seal, metrics, mmap_cache, template, ret);
}
int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint64_t p, uint64_t *seqnum, Object **ret, uint64_t *offset) {
@@ -2964,16 +2969,35 @@ int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint6
return r;
}
+void journal_reset_metrics(JournalMetrics *m) {
+ assert(m);
+
+ /* Set everything to "pick automatic values". */
+
+ *m = (JournalMetrics) {
+ .min_use = (uint64_t) -1,
+ .max_use = (uint64_t) -1,
+ .min_size = (uint64_t) -1,
+ .max_size = (uint64_t) -1,
+ .keep_free = (uint64_t) -1,
+ .n_max_files = (uint64_t) -1,
+ };
+}
+
void journal_default_metrics(JournalMetrics *m, int fd) {
- uint64_t fs_size = 0;
+ char a[FORMAT_BYTES_MAX], b[FORMAT_BYTES_MAX], c[FORMAT_BYTES_MAX], d[FORMAT_BYTES_MAX], e[FORMAT_BYTES_MAX];
struct statvfs ss;
- char a[FORMAT_BYTES_MAX], b[FORMAT_BYTES_MAX], c[FORMAT_BYTES_MAX], d[FORMAT_BYTES_MAX];
+ uint64_t fs_size;
assert(m);
assert(fd >= 0);
if (fstatvfs(fd, &ss) >= 0)
fs_size = ss.f_frsize * ss.f_blocks;
+ else {
+ log_debug_errno(errno, "Failed to detremine disk size: %m");
+ fs_size = 0;
+ }
if (m->max_use == (uint64_t) -1) {
@@ -2990,10 +3014,16 @@ void journal_default_metrics(JournalMetrics *m, int fd) {
} else {
m->max_use = PAGE_ALIGN(m->max_use);
- if (m->max_use < JOURNAL_FILE_SIZE_MIN*2)
+ if (m->max_use != 0 && m->max_use < JOURNAL_FILE_SIZE_MIN*2)
m->max_use = JOURNAL_FILE_SIZE_MIN*2;
}
+ if (m->min_use == (uint64_t) -1)
+ m->min_use = DEFAULT_MIN_USE;
+
+ if (m->min_use > m->max_use)
+ m->min_use = m->max_use;
+
if (m->max_size == (uint64_t) -1) {
m->max_size = PAGE_ALIGN(m->max_use / 8); /* 8 chunks */
@@ -3002,11 +3032,13 @@ void journal_default_metrics(JournalMetrics *m, int fd) {
} else
m->max_size = PAGE_ALIGN(m->max_size);
- if (m->max_size < JOURNAL_FILE_SIZE_MIN)
- m->max_size = JOURNAL_FILE_SIZE_MIN;
+ if (m->max_size != 0) {
+ if (m->max_size < JOURNAL_FILE_SIZE_MIN)
+ m->max_size = JOURNAL_FILE_SIZE_MIN;
- if (m->max_size*2 > m->max_use)
- m->max_use = m->max_size*2;
+ if (m->max_use != 0 && m->max_size*2 > m->max_use)
+ m->max_use = m->max_size*2;
+ }
if (m->min_size == (uint64_t) -1)
m->min_size = JOURNAL_FILE_SIZE_MIN;
@@ -3016,7 +3048,7 @@ void journal_default_metrics(JournalMetrics *m, int fd) {
if (m->min_size < JOURNAL_FILE_SIZE_MIN)
m->min_size = JOURNAL_FILE_SIZE_MIN;
- if (m->min_size > m->max_size)
+ if (m->max_size != 0 && m->min_size > m->max_size)
m->max_size = m->min_size;
}
@@ -3032,11 +3064,16 @@ void journal_default_metrics(JournalMetrics *m, int fd) {
m->keep_free = DEFAULT_KEEP_FREE;
}
- log_debug("Fixed max_use=%s max_size=%s min_size=%s keep_free=%s",
- format_bytes(a, sizeof(a), m->max_use),
- format_bytes(b, sizeof(b), m->max_size),
- format_bytes(c, sizeof(c), m->min_size),
- format_bytes(d, sizeof(d), m->keep_free));
+ if (m->n_max_files == (uint64_t) -1)
+ m->n_max_files = DEFAULT_N_MAX_FILES;
+
+ log_debug("Fixed min_use=%s max_use=%s max_size=%s min_size=%s keep_free=%s n_max_files=%" PRIu64,
+ format_bytes(a, sizeof(a), m->min_use),
+ format_bytes(b, sizeof(b), m->max_use),
+ format_bytes(c, sizeof(c), m->max_size),
+ format_bytes(d, sizeof(d), m->min_size),
+ format_bytes(e, sizeof(e), m->keep_free),
+ m->n_max_files);
}
int journal_file_get_cutoff_realtime_usec(JournalFile *f, usec_t *from, usec_t *to) {
diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h
index e92b75eabe..46c1f3278e 100644
--- a/src/journal/journal-file.h
+++ b/src/journal/journal-file.h
@@ -29,18 +29,20 @@
#include "sd-id128.h"
-#include "sparse-endian.h"
+#include "hashmap.h"
#include "journal-def.h"
#include "macro.h"
#include "mmap-cache.h"
-#include "hashmap.h"
+#include "sparse-endian.h"
typedef struct JournalMetrics {
- uint64_t max_use;
- uint64_t use;
- uint64_t max_size;
- uint64_t min_size;
- uint64_t keep_free;
+ /* For all these: -1 means "pick automatically", and 0 means "no limit enforced" */
+ uint64_t max_size; /* how large journal files grow at max */
+ uint64_t min_size; /* how large journal files grow at least */
+ uint64_t max_use; /* how much disk space to use in total at max, keep_free permitting */
+ uint64_t min_use; /* how much disk space to use in total at least, even if keep_free says not to */
+ uint64_t keep_free; /* how much to keep free on disk */
+ uint64_t n_max_files; /* how many files to keep around at max */
} JournalMetrics;
typedef enum direction {
@@ -136,7 +138,7 @@ int journal_file_open(
JournalFile **ret);
int journal_file_set_offline(JournalFile *f);
-void journal_file_close(JournalFile *j);
+JournalFile* journal_file_close(JournalFile *j);
int journal_file_open_reliably(
const char *fname,
@@ -223,6 +225,7 @@ int journal_file_rotate(JournalFile **f, bool compress, bool seal);
void journal_file_post_change(JournalFile *f);
+void journal_reset_metrics(JournalMetrics *m);
void journal_default_metrics(JournalMetrics *m, int fd);
int journal_file_get_cutoff_realtime_usec(JournalFile *f, usec_t *from, usec_t *to);
@@ -232,3 +235,8 @@ bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec);
int journal_file_map_data_hash_table(JournalFile *f);
int journal_file_map_field_hash_table(JournalFile *f);
+
+static inline bool JOURNAL_FILE_COMPRESS(JournalFile *f) {
+ assert(f);
+ return f->compress_xz || f->compress_lz4;
+}
diff --git a/src/journal/journal-internal.h b/src/journal/journal-internal.h
index b51ecdb600..9ff4fea7fb 100644
--- a/src/journal/journal-internal.h
+++ b/src/journal/journal-internal.h
@@ -21,18 +21,18 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
#include <inttypes.h>
#include <stdbool.h>
+#include <sys/types.h>
-#include "systemd/sd-id128.h"
+#include "sd-id128.h"
+#include "sd-journal.h"
+#include "hashmap.h"
#include "journal-def.h"
+#include "journal-file.h"
#include "list.h"
-#include "hashmap.h"
#include "set.h"
-#include "journal-file.h"
-#include "sd-journal.h"
typedef struct Match Match;
typedef struct Location Location;
@@ -121,14 +121,11 @@ struct sd_journal {
Hashmap *directories_by_path;
Hashmap *directories_by_wd;
- Set *errors;
+ Hashmap *errors;
};
char *journal_make_match_string(sd_journal *j);
void journal_print_header(sd_journal *j);
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_journal*, sd_journal_close);
-#define _cleanup_journal_close_ _cleanup_(sd_journal_closep)
-
#define JOURNAL_FOREACH_DATA_RETVAL(j, data, l, retval) \
for (sd_journal_restart_data(j); ((retval) = sd_journal_enumerate_data((j), &(data), &(l))) > 0; )
diff --git a/src/journal/journal-qrcode.c b/src/journal/journal-qrcode.c
index 1db66e89c6..257ddb302b 100644
--- a/src/journal/journal-qrcode.c
+++ b/src/journal/journal-qrcode.c
@@ -20,12 +20,11 @@
***/
#include <assert.h>
-#include <stdio.h>
#include <errno.h>
-#include <stdlib.h>
-#include <stdbool.h>
-
#include <qrencode.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
#include "journal-qrcode.h"
diff --git a/src/journal/journal-qrcode.h b/src/journal/journal-qrcode.h
index 3ff6a3ad4a..7d14e8754b 100644
--- a/src/journal/journal-qrcode.h
+++ b/src/journal/journal-qrcode.h
@@ -21,8 +21,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <inttypes.h>
#include <stdio.h>
-#include "systemd/sd-id128.h"
+#include "sd-id128.h"
int print_qr_code(FILE *f, const void *seed, size_t seed_size, uint64_t start, uint64_t interval, const char *hn, sd_id128_t machine);
diff --git a/src/journal/journal-send.c b/src/journal/journal-send.c
index 1e3a463504..44fa11a00e 100644
--- a/src/journal/journal-send.c
+++ b/src/journal/journal-send.c
@@ -19,20 +19,27 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/socket.h>
-#include <sys/un.h>
#include <errno.h>
-#include <stddef.h>
-#include <unistd.h>
#include <fcntl.h>
#include <printf.h>
+#include <stddef.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
#define SD_JOURNAL_SUPPRESS_LOCATION
#include "sd-journal.h"
-#include "util.h"
-#include "socket-util.h"
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "io-util.h"
#include "memfd-util.h"
+#include "socket-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
+#include "util.h"
#define SNDBUF_SIZE (8*1024*1024)
@@ -212,19 +219,14 @@ _public_ int sd_journal_sendv(const struct iovec *iov, int n) {
.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(sa.sun_path),
};
ssize_t k;
- union {
- struct cmsghdr cmsghdr;
- uint8_t buf[CMSG_SPACE(sizeof(int))];
- } control;
- struct cmsghdr *cmsg;
bool have_syslog_identifier = false;
bool seal = true;
assert_return(iov, -EINVAL);
assert_return(n > 0, -EINVAL);
- w = alloca(sizeof(struct iovec) * n * 5 + 3);
- l = alloca(sizeof(uint64_t) * n);
+ w = newa(struct iovec, n * 5 + 3);
+ l = newa(uint64_t, n);
for (i = 0; i < n; i++) {
char *c, *nl;
@@ -335,26 +337,11 @@ _public_ int sd_journal_sendv(const struct iovec *iov, int n) {
return r;
}
- mh.msg_iov = NULL;
- mh.msg_iovlen = 0;
-
- zero(control);
- mh.msg_control = &control;
- mh.msg_controllen = sizeof(control);
-
- cmsg = CMSG_FIRSTHDR(&mh);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(sizeof(int));
- memcpy(CMSG_DATA(cmsg), &buffer_fd, sizeof(int));
-
- mh.msg_controllen = cmsg->cmsg_len;
-
- k = sendmsg(fd, &mh, MSG_NOSIGNAL);
- if (k < 0)
- return -errno;
-
- return 0;
+ r = send_one_fd_sa(fd, buffer_fd, mh.msg_name, mh.msg_namelen, 0);
+ if (r == -ENOENT)
+ /* Fail silently if the journal is not available */
+ return 0;
+ return r;
}
static int fill_iovec_perror_and_send(const char *message, int skip, struct iovec iov[]) {
diff --git a/src/journal/journal-vacuum.c b/src/journal/journal-vacuum.c
index 17499bbc30..4b5fc76eb1 100644
--- a/src/journal/journal-vacuum.c
+++ b/src/journal/journal-vacuum.c
@@ -23,20 +23,27 @@
#include <sys/stat.h>
#include <unistd.h>
+#include "sd-id128.h"
+
+#include "alloc-util.h"
+#include "dirent-util.h"
+#include "fd-util.h"
#include "journal-def.h"
#include "journal-file.h"
#include "journal-vacuum.h"
-#include "sd-id128.h"
+#include "parse-util.h"
+#include "string-util.h"
#include "util.h"
+#include "xattr-util.h"
struct vacuum_info {
uint64_t usage;
char *filename;
uint64_t realtime;
+
sd_id128_t seqnum_id;
uint64_t seqnum;
-
bool have_seqnum;
};
@@ -67,19 +74,18 @@ static int vacuum_compare(const void *_a, const void *_b) {
}
static void patch_realtime(
- const char *dir,
+ int fd,
const char *fn,
const struct stat *st,
unsigned long long *realtime) {
- _cleanup_free_ char *path = NULL;
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
* suggested... */
- assert(dir);
+ assert(fd >= 0);
assert(fn);
assert(st);
assert(realtime);
@@ -101,14 +107,7 @@ static void patch_realtime(
* unfortunately there's currently no sane API to query
* it. Hence let's implement this manually... */
- /* Unfortunately there is is not fgetxattrat(), so we need to
- * go via path here. :-( */
-
- path = strjoin(dir, "/", fn, NULL);
- if (!path)
- return;
-
- if (path_getcrtime(path, &crtime) >= 0) {
+ if (fd_getcrtime_at(fd, fn, &crtime, 0) >= 0) {
if (crtime < *realtime)
*realtime = crtime;
}
@@ -120,9 +119,13 @@ static int journal_file_empty(int dir_fd, const char *name) {
le64_t n_entries;
ssize_t n;
- fd = openat(dir_fd, name, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK);
- if (fd < 0)
- return -errno;
+ fd = openat(dir_fd, name, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK|O_NOATIME);
+ if (fd < 0) {
+ /* Maybe failed due to O_NOATIME and lack of privileges? */
+ fd = openat(dir_fd, name, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK);
+ if (fd < 0)
+ return -errno;
+ }
if (fstat(fd, &st) < 0)
return -errno;
@@ -144,22 +147,24 @@ static int journal_file_empty(int dir_fd, const char *name) {
int journal_directory_vacuum(
const char *directory,
uint64_t max_use,
+ uint64_t n_max_files,
usec_t max_retention_usec,
usec_t *oldest_usec,
bool verbose) {
_cleanup_closedir_ DIR *d = NULL;
- int r = 0;
struct vacuum_info *list = NULL;
- unsigned n_list = 0, i;
+ unsigned n_list = 0, i, n_active_files = 0;
size_t n_allocated = 0;
uint64_t sum = 0, freed = 0;
usec_t retention_limit = 0;
char sbytes[FORMAT_BYTES_MAX];
+ struct dirent *de;
+ int r;
assert(directory);
- if (max_use <= 0 && max_retention_usec <= 0)
+ if (max_use <= 0 && max_retention_usec <= 0 && n_max_files <= 0)
return 0;
if (max_retention_usec > 0) {
@@ -174,27 +179,20 @@ int journal_directory_vacuum(
if (!d)
return -errno;
- for (;;) {
- struct dirent *de;
- size_t q;
- struct stat st;
- char *p;
+ FOREACH_DIRENT_ALL(de, d, r = -errno; goto finish) {
+
unsigned long long seqnum = 0, realtime;
+ _cleanup_free_ char *p = NULL;
sd_id128_t seqnum_id;
bool have_seqnum;
+ uint64_t size;
+ struct stat st;
+ size_t q;
- errno = 0;
- de = readdir(d);
- if (!de && errno != 0) {
- r = -errno;
- goto finish;
- }
-
- if (!de)
- break;
-
- if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0)
+ if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
+ log_debug_errno(errno, "Failed to stat file %s while vacuuming, ignoring: %m", de->d_name);
continue;
+ }
if (!S_ISREG(st.st_mode))
continue;
@@ -203,15 +201,20 @@ int journal_directory_vacuum(
if (endswith(de->d_name, ".journal")) {
- /* Vacuum archived files */
+ /* Vacuum archived files. Active files are
+ * left around */
- if (q < 1 + 32 + 1 + 16 + 1 + 16 + 8)
+ if (q < 1 + 32 + 1 + 16 + 1 + 16 + 8) {
+ n_active_files++;
continue;
+ }
if (de->d_name[q-8-16-1] != '-' ||
de->d_name[q-8-16-1-16-1] != '-' ||
- de->d_name[q-8-16-1-16-1-32-1] != '@')
+ de->d_name[q-8-16-1-16-1-32-1] != '@') {
+ n_active_files++;
continue;
+ }
p = strdup(de->d_name);
if (!p) {
@@ -221,12 +224,12 @@ int journal_directory_vacuum(
de->d_name[q-8-16-1-16-1] = 0;
if (sd_id128_from_string(de->d_name + q-8-16-1-16-1-32, &seqnum_id) < 0) {
- free(p);
+ n_active_files++;
continue;
}
if (sscanf(de->d_name + q-8-16-1-16, "%16llx-%16llx.journal", &seqnum, &realtime) != 2) {
- free(p);
+ n_active_files++;
continue;
}
@@ -237,12 +240,16 @@ int journal_directory_vacuum(
/* Vacuum corrupted files */
- if (q < 1 + 16 + 1 + 16 + 8 + 1)
+ if (q < 1 + 16 + 1 + 16 + 8 + 1) {
+ n_active_files ++;
continue;
+ }
if (de->d_name[q-1-8-16-1] != '-' ||
- de->d_name[q-1-8-16-1-16-1] != '@')
+ de->d_name[q-1-8-16-1-16-1] != '@') {
+ n_active_files ++;
continue;
+ }
p = strdup(de->d_name);
if (!p) {
@@ -251,55 +258,68 @@ int journal_directory_vacuum(
}
if (sscanf(de->d_name + q-1-8-16-1-16, "%16llx-%16llx.journal~", &realtime, &tmp) != 2) {
- free(p);
+ n_active_files ++;
continue;
}
have_seqnum = false;
- } else
- /* We do not vacuum active files or unknown files! */
+ } else {
+ /* We do not vacuum unknown files! */
+ log_debug("Not vacuuming unknown file %s.", de->d_name);
continue;
+ }
- if (journal_file_empty(dirfd(d), p)) {
- /* Always vacuum empty non-online files. */
+ size = 512UL * (uint64_t) st.st_blocks;
- uint64_t size = 512UL * (uint64_t) st.st_blocks;
+ r = journal_file_empty(dirfd(d), p);
+ if (r < 0) {
+ log_debug_errno(r, "Failed check if %s is empty, ignoring: %m", p);
+ continue;
+ }
+ if (r > 0) {
+ /* Always vacuum empty non-online files. */
if (unlinkat(dirfd(d), p, 0) >= 0) {
- log_full(verbose ? LOG_INFO : LOG_DEBUG, "Deleted empty archived journal %s/%s (%s).", directory, p, format_bytes(sbytes, sizeof(sbytes), size));
+
+ log_full(verbose ? LOG_INFO : LOG_DEBUG,
+ "Deleted empty archived journal %s/%s (%s).", directory, p, format_bytes(sbytes, sizeof(sbytes), size));
+
freed += size;
} else if (errno != ENOENT)
log_warning_errno(errno, "Failed to delete empty archived journal %s/%s: %m", directory, p);
- free(p);
continue;
}
- patch_realtime(directory, p, &st, &realtime);
+ patch_realtime(dirfd(d), p, &st, &realtime);
if (!GREEDY_REALLOC(list, n_allocated, n_list + 1)) {
- free(p);
r = -ENOMEM;
goto finish;
}
list[n_list].filename = p;
- list[n_list].usage = 512UL * (uint64_t) st.st_blocks;
+ list[n_list].usage = size;
list[n_list].seqnum = seqnum;
list[n_list].realtime = realtime;
list[n_list].seqnum_id = seqnum_id;
list[n_list].have_seqnum = have_seqnum;
-
- sum += list[n_list].usage;
-
n_list ++;
+
+ p = NULL;
+ sum += size;
}
qsort_safe(list, n_list, sizeof(struct vacuum_info), vacuum_compare);
for (i = 0; i < n_list; i++) {
+ unsigned left;
+
+ left = n_active_files + n_list - i;
+
if ((max_retention_usec <= 0 || list[i].realtime >= retention_limit) &&
- (max_use <= 0 || sum <= max_use))
+ (max_use <= 0 || sum <= max_use) &&
+ (n_max_files <= 0 || left <= n_max_files))
break;
if (unlinkat(dirfd(d), list[i].filename, 0) >= 0) {
@@ -318,6 +338,8 @@ int journal_directory_vacuum(
if (oldest_usec && i < n_list && (*oldest_usec == 0 || list[i].realtime < *oldest_usec))
*oldest_usec = list[i].realtime;
+ r = 0;
+
finish:
for (i = 0; i < n_list; i++)
free(list[i].filename);
diff --git a/src/journal/journal-vacuum.h b/src/journal/journal-vacuum.h
index c45cc31d0e..49ab90af91 100644
--- a/src/journal/journal-vacuum.h
+++ b/src/journal/journal-vacuum.h
@@ -21,5 +21,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <inttypes.h>
+#include <stdbool.h>
-int journal_directory_vacuum(const char *directory, uint64_t max_use, usec_t max_retention_usec, usec_t *oldest_usec, bool vacuum);
+#include "time-util.h"
+
+int journal_directory_vacuum(const char *directory, uint64_t max_use, uint64_t n_max_files, usec_t max_retention_usec, usec_t *oldest_usec, bool verbose);
diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c
index 32d59c716f..715847e018 100644
--- a/src/journal/journal-verify.c
+++ b/src/journal/journal-verify.c
@@ -19,20 +19,23 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
-#include <sys/mman.h>
#include <fcntl.h>
#include <stddef.h>
+#include <sys/mman.h>
+#include <unistd.h>
-#include "util.h"
-#include "macro.h"
+#include "alloc-util.h"
+#include "compress.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "journal-authenticate.h"
#include "journal-def.h"
#include "journal-file.h"
-#include "journal-authenticate.h"
#include "journal-verify.h"
#include "lookup3.h"
-#include "compress.h"
+#include "macro.h"
#include "terminal-util.h"
+#include "util.h"
static void draw_progress(uint64_t p, usec_t *last_usec) {
unsigned n, i, j, k;
@@ -839,19 +842,19 @@ int journal_file_verify(
data_fd = open_tmpfile("/var/tmp", O_RDWR | O_CLOEXEC);
if (data_fd < 0) {
- r = log_error_errno(errno, "Failed to create data file: %m");
+ r = log_error_errno(data_fd, "Failed to create data file: %m");
goto fail;
}
entry_fd = open_tmpfile("/var/tmp", O_RDWR | O_CLOEXEC);
if (entry_fd < 0) {
- r = log_error_errno(errno, "Failed to create entry file: %m");
+ r = log_error_errno(entry_fd, "Failed to create entry file: %m");
goto fail;
}
entry_array_fd = open_tmpfile("/var/tmp", O_RDWR | O_CLOEXEC);
if (entry_array_fd < 0) {
- r = log_error_errno(errno,
+ r = log_error_errno(entry_array_fd,
"Failed to create entry array file: %m");
goto fail;
}
@@ -897,7 +900,7 @@ int journal_file_verify(
r = journal_file_object_verify(f, p, o);
if (r < 0) {
- error(p, "Envalid object contents: %s", strerror(-r));
+ error(p, "Invalid object contents: %s", strerror(-r));
goto fail;
}
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index b38b151485..db11421e7a 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -19,48 +19,58 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <locale.h>
+#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
-#include <errno.h>
+#include <getopt.h>
+#include <linux/fs.h>
+#include <locale.h>
+#include <poll.h>
+#include <signal.h>
#include <stddef.h>
-#include <string.h>
#include <stdio.h>
-#include <unistd.h>
#include <stdlib.h>
-#include <getopt.h>
-#include <signal.h>
-#include <poll.h>
-#include <sys/stat.h>
+#include <string.h>
#include <sys/inotify.h>
-#include <linux/fs.h>
+#include <sys/stat.h>
+#include <unistd.h>
-#include "sd-journal.h"
#include "sd-bus.h"
-#include "log.h"
-#include "logs-show.h"
-#include "util.h"
+#include "sd-journal.h"
+
#include "acl-util.h"
-#include "path-util.h"
+#include "alloc-util.h"
+#include "bus-error.h"
+#include "bus-util.h"
+#include "catalog.h"
+#include "chattr-util.h"
+#include "fd-util.h"
#include "fileio.h"
-#include "build.h"
-#include "pager.h"
-#include "strv.h"
-#include "set.h"
-#include "sigbus.h"
-#include "journal-internal.h"
+#include "fs-util.h"
+#include "fsprg.h"
+#include "glob-util.h"
+#include "hostname-util.h"
+#include "io-util.h"
#include "journal-def.h"
-#include "journal-verify.h"
+#include "journal-internal.h"
#include "journal-qrcode.h"
#include "journal-vacuum.h"
-#include "fsprg.h"
-#include "unit-name.h"
-#include "catalog.h"
+#include "journal-verify.h"
+#include "locale-util.h"
+#include "log.h"
+#include "logs-show.h"
#include "mkdir.h"
-#include "bus-util.h"
-#include "bus-error.h"
+#include "pager.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "rlimit-util.h"
+#include "set.h"
+#include "sigbus.h"
+#include "strv.h"
+#include "syslog-util.h"
#include "terminal-util.h"
-#include "hostname-util.h"
+#include "unit-name.h"
+#include "user-util.h"
#define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
@@ -105,10 +115,11 @@ static const char *arg_field = NULL;
static bool arg_catalog = false;
static bool arg_reverse = false;
static int arg_journal_type = 0;
-static const char *arg_root = NULL;
+static char *arg_root = NULL;
static const char *arg_machine = NULL;
-static uint64_t arg_vacuum_size = (uint64_t) -1;
-static usec_t arg_vacuum_time = USEC_INFINITY;
+static uint64_t arg_vacuum_size = 0;
+static uint64_t arg_vacuum_n_files = 0;
+static usec_t arg_vacuum_time = 0;
static enum {
ACTION_SHOW,
@@ -122,6 +133,8 @@ static enum {
ACTION_UPDATE_CATALOG,
ACTION_LIST_BOOTS,
ACTION_FLUSH,
+ ACTION_SYNC,
+ ACTION_ROTATE,
ACTION_VACUUM,
} arg_action = ACTION_SHOW;
@@ -189,12 +202,12 @@ static void help(void) {
printf("%s [OPTIONS...] [MATCHES...]\n\n"
"Query the journal.\n\n"
- "Flags:\n"
+ "Options:\n"
" --system Show the system journal\n"
" --user Show the user journal for the current user\n"
" -M --machine=CONTAINER Operate on local container\n"
- " --since=DATE Show entries not older than the specified date\n"
- " --until=DATE Show entries not newer than the specified date\n"
+ " -S --since=DATE Show entries not older than the specified date\n"
+ " -U --until=DATE Show entries not newer than the specified date\n"
" -c --cursor=CURSOR Show entries starting at the specified cursor\n"
" --after-cursor=CURSOR Show entries after the specified cursor\n"
" --show-cursor Print the cursor after all the entries\n"
@@ -217,12 +230,12 @@ static void help(void) {
" -x --catalog Add message explanations where available\n"
" --no-full Ellipsize fields\n"
" -a --all Show all fields, including long and unprintable\n"
- " -q --quiet Do not show privilege warning\n"
+ " -q --quiet Do not show info messages and privilege warning\n"
" --no-pager Do not pipe output into a pager\n"
" -m --merge Show entries from all available journals\n"
" -D --directory=PATH Show journal files from directory\n"
" --file=PATH Show journal file\n"
- " --root=ROOT Operate on catalog files underneath the root ROOT\n"
+ " --root=ROOT Operate on catalog files below a root directory\n"
#ifdef HAVE_GCRYPT
" --interval=TIME Time interval for changing the FSS sealing key\n"
" --verify-key=KEY Specify FSS verification key\n"
@@ -232,18 +245,21 @@ static void help(void) {
" -h --help Show this help text\n"
" --version Show package version\n"
" -F --field=FIELD List all values that a specified field takes\n"
- " --new-id128 Generate a new 128-bit ID\n"
" --disk-usage Show total disk usage of all journal files\n"
" --vacuum-size=BYTES Reduce disk usage below specified size\n"
- " --vacuum-time=TIME Remove journal files older than specified date\n"
+ " --vacuum-files=INT Leave only the specified number of journal files\n"
+ " --vacuum-time=TIME Remove journal files older than specified time\n"
+ " --verify Verify journal file consistency\n"
+ " --sync Synchronize unwritten journal messages to disk\n"
" --flush Flush all journal data from /run into /var\n"
+ " --rotate Request immediate rotation of the journal files\n"
" --header Show journal header information\n"
" --list-catalog Show all message IDs in the catalog\n"
" --dump-catalog Show entries in the message catalog\n"
" --update-catalog Update the message catalog database\n"
+ " --new-id128 Generate a new 128-bit ID\n"
#ifdef HAVE_GCRYPT
" --setup-keys Generate a new FSS key pair\n"
- " --verify Verify journal file consistency\n"
#endif
, program_invocation_short_name);
}
@@ -267,8 +283,6 @@ static int parse_argv(int argc, char *argv[]) {
ARG_VERIFY,
ARG_VERIFY_KEY,
ARG_DISK_USAGE,
- ARG_SINCE,
- ARG_UNTIL,
ARG_AFTER_CURSOR,
ARG_SHOW_CURSOR,
ARG_USER_UNIT,
@@ -277,8 +291,11 @@ static int parse_argv(int argc, char *argv[]) {
ARG_UPDATE_CATALOG,
ARG_FORCE,
ARG_UTC,
+ ARG_SYNC,
ARG_FLUSH,
+ ARG_ROTATE,
ARG_VACUUM_SIZE,
+ ARG_VACUUM_FILES,
ARG_VACUUM_TIME,
};
@@ -318,8 +335,8 @@ static int parse_argv(int argc, char *argv[]) {
{ "cursor", required_argument, NULL, 'c' },
{ "after-cursor", required_argument, NULL, ARG_AFTER_CURSOR },
{ "show-cursor", no_argument, NULL, ARG_SHOW_CURSOR },
- { "since", required_argument, NULL, ARG_SINCE },
- { "until", required_argument, NULL, ARG_UNTIL },
+ { "since", required_argument, NULL, 'S' },
+ { "until", required_argument, NULL, 'U' },
{ "unit", required_argument, NULL, 'u' },
{ "user-unit", required_argument, NULL, ARG_USER_UNIT },
{ "field", required_argument, NULL, 'F' },
@@ -331,7 +348,10 @@ static int parse_argv(int argc, char *argv[]) {
{ "machine", required_argument, NULL, 'M' },
{ "utc", no_argument, NULL, ARG_UTC },
{ "flush", no_argument, NULL, ARG_FLUSH },
+ { "sync", no_argument, NULL, ARG_SYNC },
+ { "rotate", no_argument, NULL, ARG_ROTATE },
{ "vacuum-size", required_argument, NULL, ARG_VACUUM_SIZE },
+ { "vacuum-files", required_argument, NULL, ARG_VACUUM_FILES },
{ "vacuum-time", required_argument, NULL, ARG_VACUUM_TIME },
{}
};
@@ -341,7 +361,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "hefo:aln::qmb::kD:p:c:t:u:F:xrM:", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "hefo:aln::qmb::kD:p:c:S:U:t:u:F:xrM:", options, NULL)) >= 0)
switch (c) {
@@ -350,9 +370,7 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case ARG_NO_PAGER:
arg_no_pager = true;
@@ -502,7 +520,9 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_ROOT:
- arg_root = optarg;
+ r = parse_path_argument_and_warn(optarg, true, &arg_root);
+ if (r < 0)
+ return r;
break;
case 'c':
@@ -539,6 +559,16 @@ static int parse_argv(int argc, char *argv[]) {
arg_action = ACTION_VACUUM;
break;
+ case ARG_VACUUM_FILES:
+ r = safe_atou64(optarg, &arg_vacuum_n_files);
+ if (r < 0) {
+ log_error("Failed to parse vacuum files: %s", optarg);
+ return r;
+ }
+
+ arg_action = ACTION_VACUUM;
+ break;
+
case ARG_VACUUM_TIME:
r = parse_sec(optarg, &arg_vacuum_time);
if (r < 0) {
@@ -631,7 +661,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
}
- case ARG_SINCE:
+ case 'S':
r = parse_timestamp(optarg, &arg_since);
if (r < 0) {
log_error("Failed to parse timestamp: %s", optarg);
@@ -640,7 +670,7 @@ static int parse_argv(int argc, char *argv[]) {
arg_since_set = true;
break;
- case ARG_UNTIL:
+ case 'U':
r = parse_timestamp(optarg, &arg_until);
if (r < 0) {
log_error("Failed to parse timestamp: %s", optarg);
@@ -699,6 +729,14 @@ static int parse_argv(int argc, char *argv[]) {
arg_action = ACTION_FLUSH;
break;
+ case ARG_ROTATE:
+ arg_action = ACTION_ROTATE;
+ break;
+
+ case ARG_SYNC:
+ arg_action = ACTION_SYNC;
+ break;
+
case '?':
return -EINVAL;
@@ -729,7 +767,7 @@ static int parse_argv(int argc, char *argv[]) {
return -EINVAL;
}
- if (arg_action != ACTION_SHOW && optind < argc) {
+ if (!IN_SET(arg_action, ACTION_SHOW, ACTION_DUMP_CATALOG, ACTION_LIST_CATALOG) && optind < argc) {
log_error("Extraneous arguments starting with '%s'", argv[optind]);
return -EINVAL;
}
@@ -1453,7 +1491,7 @@ static int setup_keys(void) {
safe_close(fd);
fd = mkostemp_safe(k, O_WRONLY|O_CLOEXEC);
if (fd < 0) {
- r = log_error_errno(errno, "Failed to open %s: %m", k);
+ r = log_error_errno(fd, "Failed to open %s: %m", k);
goto finish;
}
@@ -1461,7 +1499,7 @@ static int setup_keys(void) {
* writing and in-place updating */
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");
+ log_warning_errno(r, "Failed to set file attributes: %m");
zero(h);
memcpy(h.signature, "KSHHRHLP", 8);
@@ -1580,7 +1618,7 @@ static int verify(sd_journal *j) {
/* If the key was invalid give up right-away. */
return k;
} else if (k < 0) {
- log_warning("FAIL: %s (%s)", f->path, strerror(-k));
+ log_warning_errno(k, "FAIL: %s (%m)", f->path);
r = k;
} else {
char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX];
@@ -1678,54 +1716,73 @@ static int access_check_var_log_journal(sd_journal *j) {
static int access_check(sd_journal *j) {
Iterator it;
void *code;
+ char *path;
int r = 0;
assert(j);
- if (set_isempty(j->errors)) {
+ if (hashmap_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))) {
+ if (hashmap_contains(j->errors, INT_TO_PTR(-EACCES))) {
(void) access_check_var_log_journal(j);
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) {
+ HASHMAP_FOREACH_KEY(path, code, j->errors, it) {
int err;
- err = -PTR_TO_INT(code);
- assert(err > 0);
+ err = abs(PTR_TO_INT(code));
- if (err == EACCES)
+ switch (err) {
+ case EACCES:
continue;
- log_warning_errno(err, "Error was encountered while opening journal files: %m");
- if (r == 0)
- r = -err;
+ case ENODATA:
+ log_warning_errno(err, "Journal file %s is truncated, ignoring file.", path);
+ break;
+
+ case EPROTONOSUPPORT:
+ log_warning_errno(err, "Journal file %s uses an unsupported feature, ignoring file.", path);
+ break;
+
+ case EBADMSG:
+ log_warning_errno(err, "Journal file %s corrupted, ignoring file.", path);
+ break;
+
+ default:
+ log_warning_errno(err, "An error was encountered while opening journal file %s, ignoring file.", path);
+ break;
+ }
}
return r;
}
static int flush_to_var(void) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_close_ int watch_fd = -1;
int r;
+ if (arg_machine) {
+ log_error("--flush is not supported in conjunction with --machine=.");
+ return -EOPNOTSUPP;
+ }
+
/* Quick exit */
if (access("/run/systemd/journal/flushed", F_OK) >= 0)
return 0;
/* OK, let's actually do the full logic, send SIGUSR1 to the
* daemon and set up inotify to wait for the flushed file to appear */
- r = bus_open_system_systemd(&bus);
+ r = bus_connect_system_systemd(&bus);
if (r < 0)
return log_error_errno(r, "Failed to get D-Bus connection: %m");
@@ -1738,10 +1795,8 @@ static int flush_to_var(void) {
&error,
NULL,
"ssi", "systemd-journald.service", "main", SIGUSR1);
- if (r < 0) {
- log_error("Failed to kill journal service: %s", bus_error_message(&error, r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to kill journal service: %s", bus_error_message(&error, r));
mkdir_p("/run/systemd/journal", 0755);
@@ -1772,9 +1827,100 @@ static int flush_to_var(void) {
return 0;
}
+static int send_signal_and_wait(int sig, const char *watch_path) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_close_ int watch_fd = -1;
+ usec_t start;
+ int r;
+
+ if (arg_machine) {
+ log_error("--sync and --rotate are not supported in conjunction with --machine=.");
+ return -EOPNOTSUPP;
+ }
+
+ start = now(CLOCK_MONOTONIC);
+
+ /* This call sends the specified signal to journald, and waits
+ * for acknowledgment by watching the mtime of the specified
+ * flag file. This is used to trigger syncing or rotation and
+ * then wait for the operation to complete. */
+
+ for (;;) {
+ usec_t tstamp;
+
+ /* See if a sync happened by now. */
+ r = read_timestamp_file(watch_path, &tstamp);
+ if (r < 0 && r != -ENOENT)
+ return log_error_errno(errno, "Failed to read %s: %m", watch_path);
+ if (r >= 0 && tstamp >= start)
+ return 0;
+
+ /* Let's ask for a sync, but only once. */
+ if (!bus) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+
+ r = bus_connect_system_systemd(&bus);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get D-Bus connection: %m");
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "KillUnit",
+ &error,
+ NULL,
+ "ssi", "systemd-journald.service", "main", sig);
+ if (r < 0)
+ return log_error_errno(r, "Failed to kill journal service: %s", bus_error_message(&error, r));
+
+ continue;
+ }
+
+ /* Let's install the inotify watch, if we didn't do that yet. */
+ if (watch_fd < 0) {
+
+ mkdir_p("/run/systemd/journal", 0755);
+
+ watch_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
+ if (watch_fd < 0)
+ return log_error_errno(errno, "Failed to create inotify watch: %m");
+
+ r = inotify_add_watch(watch_fd, "/run/systemd/journal", IN_MOVED_TO|IN_DONT_FOLLOW|IN_ONLYDIR);
+ if (r < 0)
+ return log_error_errno(errno, "Failed to watch journal directory: %m");
+
+ /* Recheck the flag file immediately, so that we don't miss any event since the last check. */
+ continue;
+ }
+
+ /* OK, all preparatory steps done, let's wait until
+ * inotify reports an event. */
+
+ r = fd_wait_for_event(watch_fd, POLLIN, USEC_INFINITY);
+ if (r < 0)
+ return log_error_errno(r, "Failed to wait for event: %m");
+
+ r = flush_fd(watch_fd);
+ if (r < 0)
+ return log_error_errno(r, "Failed to flush inotify events: %m");
+ }
+
+ return 0;
+}
+
+static int rotate(void) {
+ return send_signal_and_wait(SIGUSR2, "/run/systemd/journal/rotated");
+}
+
+static int sync_journal(void) {
+ return send_signal_and_wait(SIGRTMIN+1, "/run/systemd/journal/synced");
+}
+
int main(int argc, char *argv[]) {
int r;
- _cleanup_journal_close_ sd_journal *j = NULL;
+ _cleanup_(sd_journal_closep) sd_journal *j = NULL;
bool need_seek = false;
sd_id128_t previous_boot_id;
bool previous_boot_id_valid = false, first_line = true;
@@ -1797,25 +1943,19 @@ int main(int argc, char *argv[]) {
* be split up into many files. */
setrlimit_closest(RLIMIT_NOFILE, &RLIMIT_MAKE_CONST(16384));
- if (arg_action == ACTION_NEW_ID128) {
- r = generate_new_id128();
- goto finish;
- }
+ switch (arg_action) {
- if (arg_action == ACTION_FLUSH) {
- r = flush_to_var();
+ case ACTION_NEW_ID128:
+ r = generate_new_id128();
goto finish;
- }
- if (arg_action == ACTION_SETUP_KEYS) {
+ case ACTION_SETUP_KEYS:
r = setup_keys();
goto finish;
- }
-
- if (arg_action == ACTION_UPDATE_CATALOG ||
- arg_action == ACTION_LIST_CATALOG ||
- arg_action == ACTION_DUMP_CATALOG) {
+ case ACTION_LIST_CATALOG:
+ case ACTION_DUMP_CATALOG:
+ case ACTION_UPDATE_CATALOG: {
_cleanup_free_ char *database;
database = path_join(arg_root, CATALOG_DATABASE, NULL);
@@ -1831,9 +1971,10 @@ int main(int argc, char *argv[]) {
} else {
bool oneline = arg_action == ACTION_LIST_CATALOG;
+ pager_open_if_enabled();
+
if (optind < argc)
- r = catalog_list_items(stdout, database,
- oneline, argv + optind);
+ r = catalog_list_items(stdout, database, oneline, argv + optind);
else
r = catalog_list(stdout, database, oneline);
if (r < 0)
@@ -1843,6 +1984,31 @@ int main(int argc, char *argv[]) {
goto finish;
}
+ case ACTION_FLUSH:
+ r = flush_to_var();
+ goto finish;
+
+ case ACTION_SYNC:
+ r = sync_journal();
+ goto finish;
+
+ case ACTION_ROTATE:
+ r = rotate();
+ goto finish;
+
+ case ACTION_SHOW:
+ case ACTION_PRINT_HEADER:
+ case ACTION_VERIFY:
+ case ACTION_DISK_USAGE:
+ case ACTION_LIST_BOOTS:
+ case ACTION_VACUUM:
+ /* These ones require access to the journal files, continue below. */
+ break;
+
+ default:
+ assert_not_reached("Unknown action");
+ }
+
if (arg_directory)
r = sd_journal_open_directory(&j, arg_directory, arg_journal_type);
else if (arg_file)
@@ -1852,8 +2018,7 @@ int main(int argc, char *argv[]) {
else
r = sd_journal_open(&j, !arg_merge*SD_JOURNAL_LOCAL_ONLY + arg_journal_type);
if (r < 0) {
- log_error_errno(r, "Failed to open %s: %m",
- arg_directory ? arg_directory : arg_file ? "files" : "journal");
+ log_error_errno(r, "Failed to open %s: %m", arg_directory ?: arg_file ? "files" : "journal");
goto finish;
}
@@ -1861,18 +2026,28 @@ int main(int argc, char *argv[]) {
if (r < 0)
goto finish;
- if (arg_action == ACTION_VERIFY) {
- r = verify(j);
- goto finish;
- }
+ switch (arg_action) {
- if (arg_action == ACTION_PRINT_HEADER) {
+ case ACTION_NEW_ID128:
+ case ACTION_SETUP_KEYS:
+ case ACTION_LIST_CATALOG:
+ case ACTION_DUMP_CATALOG:
+ case ACTION_UPDATE_CATALOG:
+ case ACTION_FLUSH:
+ case ACTION_SYNC:
+ case ACTION_ROTATE:
+ assert_not_reached("Unexpected action.");
+
+ case ACTION_PRINT_HEADER:
journal_print_header(j);
r = 0;
goto finish;
- }
- if (arg_action == ACTION_DISK_USAGE) {
+ case ACTION_VERIFY:
+ r = verify(j);
+ goto finish;
+
+ case ACTION_DISK_USAGE: {
uint64_t bytes = 0;
char sbytes[FORMAT_BYTES_MAX];
@@ -1885,7 +2060,11 @@ int main(int argc, char *argv[]) {
goto finish;
}
- if (arg_action == ACTION_VACUUM) {
+ case ACTION_LIST_BOOTS:
+ r = list_boots(j);
+ goto finish;
+
+ case ACTION_VACUUM: {
Directory *d;
Iterator i;
@@ -1895,9 +2074,9 @@ int main(int argc, char *argv[]) {
if (d->is_root)
continue;
- q = journal_directory_vacuum(d->path, arg_vacuum_size, arg_vacuum_time, NULL, true);
+ q = journal_directory_vacuum(d->path, arg_vacuum_size, arg_vacuum_n_files, arg_vacuum_time, NULL, true);
if (q < 0) {
- log_error_errno(q, "Failed to vacuum: %m");
+ log_error_errno(q, "Failed to vacuum %s: %m", d->path);
r = q;
}
}
@@ -1905,9 +2084,11 @@ int main(int argc, char *argv[]) {
goto finish;
}
- if (arg_action == ACTION_LIST_BOOTS) {
- r = list_boots(j);
- goto finish;
+ case ACTION_SHOW:
+ break;
+
+ default:
+ assert_not_reached("Unknown action");
}
/* add_boot() must be called first!
@@ -2066,7 +2247,8 @@ int main(int argc, char *argv[]) {
if (arg_follow)
need_seek = true;
else {
- printf("-- No entries --\n");
+ if (!arg_quiet)
+ printf("-- No entries --\n");
goto finish;
}
}
@@ -2154,7 +2336,7 @@ int main(int argc, char *argv[]) {
flags =
arg_all * OUTPUT_SHOW_ALL |
arg_full * OUTPUT_FULL_WIDTH |
- on_tty() * OUTPUT_COLOR |
+ colors_enabled() * OUTPUT_COLOR |
arg_catalog * OUTPUT_CATALOG |
arg_utc * OUTPUT_UTC;
@@ -2200,5 +2382,7 @@ finish:
strv_free(arg_system_units);
strv_free(arg_user_units);
+ free(arg_root);
+
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/journal/journald-audit.c b/src/journal/journald-audit.c
index fe8ae194c9..3c13fe0d67 100644
--- a/src/journal/journald-audit.c
+++ b/src/journal/journald-audit.c
@@ -19,9 +19,14 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "missing.h"
-#include "journald-audit.h"
+#include "alloc-util.h"
#include "audit-type.h"
+#include "fd-util.h"
+#include "hexdecoct.h"
+#include "io-util.h"
+#include "journald-audit.h"
+#include "missing.h"
+#include "string-util.h"
typedef struct MapField {
const char *audit_field;
diff --git a/src/journal/journald-audit.h b/src/journal/journald-audit.h
index 68cdfb3410..5c88bb6383 100644
--- a/src/journal/journald-audit.h
+++ b/src/journal/journald-audit.h
@@ -21,8 +21,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "socket-util.h"
#include "journald-server.h"
+#include "socket-util.h"
void server_process_audit_message(Server *s, const void *buffer, size_t buffer_size, const struct ucred *ucred, const union sockaddr_union *sa, socklen_t salen);
diff --git a/src/journal/journald-console.c b/src/journal/journald-console.c
index 307bdc3949..04487c29b5 100644
--- a/src/journal/journald-console.c
+++ b/src/journal/journald-console.c
@@ -19,15 +19,20 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <time.h>
#include <fcntl.h>
#include <sys/socket.h>
+#include <time.h>
+#include "alloc-util.h"
+#include "fd-util.h"
#include "fileio.h"
-#include "journald-server.h"
-#include "journald-console.h"
#include "formats-util.h"
+#include "io-util.h"
+#include "journald-console.h"
+#include "journald-server.h"
+#include "parse-util.h"
#include "process-util.h"
+#include "stdio-util.h"
#include "terminal-util.h"
static bool prefix_timestamp(void) {
@@ -101,7 +106,7 @@ void server_forward_console(
fd = open_terminal(tty, O_WRONLY|O_NOCTTY|O_CLOEXEC);
if (fd < 0) {
- log_debug_errno(errno, "Failed to open %s for logging: %m", tty);
+ log_debug_errno(fd, "Failed to open %s for logging: %m", tty);
return;
}
diff --git a/src/journal/journald-gperf.gperf b/src/journal/journald-gperf.gperf
index bf7c773009..c154610c54 100644
--- a/src/journal/journald-gperf.gperf
+++ b/src/journal/journald-gperf.gperf
@@ -24,9 +24,11 @@ Journal.RateLimitBurst, config_parse_unsigned, 0, offsetof(Server, rate_li
Journal.SystemMaxUse, config_parse_iec_uint64, 0, offsetof(Server, system_metrics.max_use)
Journal.SystemMaxFileSize, config_parse_iec_uint64, 0, offsetof(Server, system_metrics.max_size)
Journal.SystemKeepFree, config_parse_iec_uint64, 0, offsetof(Server, system_metrics.keep_free)
+Journal.SystemMaxFiles, config_parse_uint64, 0, offsetof(Server, system_metrics.n_max_files)
Journal.RuntimeMaxUse, config_parse_iec_uint64, 0, offsetof(Server, runtime_metrics.max_use)
Journal.RuntimeMaxFileSize, config_parse_iec_uint64, 0, offsetof(Server, runtime_metrics.max_size)
Journal.RuntimeKeepFree, config_parse_iec_uint64, 0, offsetof(Server, runtime_metrics.keep_free)
+Journal.RuntimeMaxFiles, config_parse_uint64, 0, offsetof(Server, runtime_metrics.n_max_files)
Journal.MaxRetentionSec, config_parse_sec, 0, offsetof(Server, max_retention_usec)
Journal.MaxFileSec, config_parse_sec, 0, offsetof(Server, max_file_usec)
Journal.ForwardToSyslog, config_parse_bool, 0, offsetof(Server, forward_to_syslog)
diff --git a/src/journal/journald-kmsg.c b/src/journal/journald-kmsg.c
index 51fe3aa50a..e048e04716 100644
--- a/src/journal/journald-kmsg.c
+++ b/src/journal/journald-kmsg.c
@@ -19,20 +19,26 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
-#include <sys/epoll.h>
#include <fcntl.h>
+#include <sys/epoll.h>
#include <sys/mman.h>
#include <sys/socket.h>
+#include <unistd.h>
-#include "systemd/sd-messages.h"
-#include <libudev.h>
+#include "libudev.h"
+#include "sd-messages.h"
-#include "journald-server.h"
+#include "escape.h"
+#include "fd-util.h"
+#include "formats-util.h"
+#include "io-util.h"
#include "journald-kmsg.h"
+#include "journald-server.h"
#include "journald-syslog.h"
-#include "formats-util.h"
+#include "parse-util.h"
#include "process-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
void server_forward_kmsg(
Server *s,
@@ -341,8 +347,7 @@ static int server_read_dev_kmsg(Server *s) {
if (errno == EAGAIN || errno == EINTR || errno == EPIPE)
return 0;
- log_error_errno(errno, "Failed to read from kernel: %m");
- return -errno;
+ return log_error_errno(errno, "Failed to read from kernel: %m");
}
dev_kmsg_record(s, buffer, l);
@@ -436,6 +441,7 @@ fail:
int server_open_kernel_seqnum(Server *s) {
_cleanup_close_ int fd;
uint64_t *p;
+ int r;
assert(s);
@@ -449,8 +455,9 @@ int server_open_kernel_seqnum(Server *s) {
return 0;
}
- if (posix_fallocate(fd, 0, sizeof(uint64_t)) < 0) {
- log_error_errno(errno, "Failed to allocate sequential number file, ignoring: %m");
+ r = posix_fallocate(fd, 0, sizeof(uint64_t));
+ if (r != 0) {
+ log_error_errno(r, "Failed to allocate sequential number file, ignoring: %m");
return 0;
}
diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c
index 3e8a7a05f6..f80a6ebfe5 100644
--- a/src/journal/journald-native.c
+++ b/src/journal/journald-native.c
@@ -19,21 +19,28 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
#include <stddef.h>
#include <sys/epoll.h>
#include <sys/mman.h>
+#include <sys/statvfs.h>
+#include <unistd.h>
-#include "socket-util.h"
-#include "path-util.h"
-#include "selinux-util.h"
-#include "journald-server.h"
-#include "journald-native.h"
-#include "journald-kmsg.h"
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "io-util.h"
#include "journald-console.h"
+#include "journald-kmsg.h"
+#include "journald-native.h"
+#include "journald-server.h"
#include "journald-syslog.h"
#include "journald-wall.h"
#include "memfd-util.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "selinux-util.h"
+#include "socket-util.h"
+#include "string-util.h"
bool valid_user_field(const char *p, size_t l, bool allow_protected) {
const char *a;
@@ -338,7 +345,7 @@ void server_process_native_file(
r = readlink_malloc(sl, &k);
if (r < 0) {
- log_error_errno(errno, "readlink(%s) failed: %m", sl);
+ log_error_errno(r, "readlink(%s) failed: %m", sl);
return;
}
@@ -393,8 +400,37 @@ void server_process_native_file(
assert_se(munmap(p, ps) >= 0);
} else {
_cleanup_free_ void *p = NULL;
+ struct statvfs vfs;
ssize_t n;
+ if (fstatvfs(fd, &vfs) < 0) {
+ log_error_errno(errno, "Failed to stat file system of passed file, ignoring: %m");
+ return;
+ }
+
+ /* Refuse operating on file systems that have
+ * mandatory locking enabled, see:
+ *
+ * https://github.com/systemd/systemd/issues/1822
+ */
+ if (vfs.f_flag & ST_MANDLOCK) {
+ log_error("Received file descriptor from file system with mandatory locking enable, refusing.");
+ return;
+ }
+
+ /* Make the fd non-blocking. On regular files this has
+ * the effect of bypassing mandatory locking. Of
+ * course, this should normally not be necessary given
+ * the check above, but let's better be safe than
+ * sorry, after all NFS is pretty confusing regarding
+ * file system flags, and we better don't trust it,
+ * and so is SMB. */
+ r = fd_nonblock(fd, true);
+ if (r < 0) {
+ log_error_errno(r, "Failed to make fd non-blocking, ignoring: %m");
+ return;
+ }
+
/* The file is not sealed, we can't map the file here, since
* clients might then truncate it and trigger a SIGBUS for
* us. So let's stupidly read it */
@@ -407,7 +443,7 @@ void server_process_native_file(
n = pread(fd, p, st.st_size, 0);
if (n < 0)
- log_error_errno(n, "Failed to read file, ignoring: %m");
+ log_error_errno(errno, "Failed to read file, ignoring: %m");
else if (n > 0)
server_process_native_message(s, p, n, ucred, tv, label, label_len);
}
@@ -444,7 +480,7 @@ int server_open_native_socket(Server*s) {
return log_error_errno(errno, "SO_PASSCRED failed: %m");
#ifdef HAVE_SELINUX
- if (mac_selinux_use()) {
+ if (mac_selinux_have()) {
r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
if (r < 0)
log_warning_errno(errno, "SO_PASSSEC failed: %m");
@@ -459,5 +495,9 @@ int server_open_native_socket(Server*s) {
if (r < 0)
return log_error_errno(r, "Failed to add native server fd to event loop: %m");
+ r = sd_event_source_set_priority(s->native_event_source, SD_EVENT_PRIORITY_NORMAL+5);
+ if (r < 0)
+ return log_error_errno(r, "Failed to adjust native event source priority: %m");
+
return 0;
}
diff --git a/src/journal/journald-rate-limit.c b/src/journal/journald-rate-limit.c
index 6f83035a4e..1c406aef8e 100644
--- a/src/journal/journald-rate-limit.c
+++ b/src/journal/journald-rate-limit.c
@@ -19,14 +19,16 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <string.h>
#include <errno.h>
+#include <string.h>
+#include "alloc-util.h"
+#include "hashmap.h"
#include "journald-rate-limit.h"
#include "list.h"
-#include "util.h"
-#include "hashmap.h"
#include "random-util.h"
+#include "string-util.h"
+#include "util.h"
#define POOLS_MAX 5
#define BUCKETS_MAX 127
@@ -57,7 +59,7 @@ struct JournalRateLimitGroup {
char *id;
JournalRateLimitPool pools[POOLS_MAX];
- unsigned long hash;
+ uint64_t hash;
LIST_FIELDS(JournalRateLimitGroup, bucket);
LIST_FIELDS(JournalRateLimitGroup, lru);
@@ -145,6 +147,7 @@ static void journal_rate_limit_vacuum(JournalRateLimit *r, usec_t ts) {
static JournalRateLimitGroup* journal_rate_limit_group_new(JournalRateLimit *r, const char *id, usec_t ts) {
JournalRateLimitGroup *g;
+ struct siphash state;
assert(r);
assert(id);
@@ -157,7 +160,9 @@ static JournalRateLimitGroup* journal_rate_limit_group_new(JournalRateLimit *r,
if (!g->id)
goto fail;
- g->hash = string_hash_func(g->id, r->hash_key);
+ siphash24_init(&state, r->hash_key);
+ string_hash_func(g->id, &state);
+ g->hash = siphash24_finalize(&state);
journal_rate_limit_vacuum(r, ts);
@@ -204,9 +209,10 @@ static unsigned burst_modulate(unsigned burst, uint64_t available) {
}
int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, uint64_t available) {
- unsigned long h;
+ uint64_t h;
JournalRateLimitGroup *g;
JournalRateLimitPool *p;
+ struct siphash state;
unsigned burst;
usec_t ts;
@@ -222,7 +228,9 @@ int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, u
ts = now(CLOCK_MONOTONIC);
- h = string_hash_func(id, r->hash_key);
+ siphash24_init(&state, r->hash_key);
+ string_hash_func(id, &state);
+ h = siphash24_finalize(&state);
g = r->buckets[h % BUCKETS_MAX];
LIST_FOREACH(bucket, g, g)
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index fa2e9b9825..cfcc2c4302 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -19,45 +19,57 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/signalfd.h>
-#include <sys/ioctl.h>
-#include <linux/sockios.h>
-#include <sys/statvfs.h>
-#include <sys/mman.h>
-
#ifdef HAVE_SELINUX
#include <selinux/selinux.h>
#endif
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/signalfd.h>
+#include <sys/statvfs.h>
+#include <linux/sockios.h>
-#include <libudev.h>
-
+#include "libudev.h"
+#include "sd-daemon.h"
#include "sd-journal.h"
#include "sd-messages.h"
-#include "sd-daemon.h"
-#include "mkdir.h"
-#include "rm-rf.h"
-#include "hashmap.h"
-#include "journal-file.h"
-#include "socket-util.h"
+
+#include "acl-util.h"
+#include "alloc-util.h"
+#include "audit-util.h"
#include "cgroup-util.h"
-#include "missing.h"
#include "conf-parser.h"
-#include "selinux-util.h"
-#include "acl-util.h"
+#include "dirent-util.h"
+#include "extract-word.h"
+#include "fd-util.h"
+#include "fileio.h"
#include "formats-util.h"
-#include "process-util.h"
+#include "fs-util.h"
+#include "hashmap.h"
#include "hostname-util.h"
-#include "signal-util.h"
+#include "io-util.h"
+#include "journal-authenticate.h"
+#include "journal-file.h"
#include "journal-internal.h"
#include "journal-vacuum.h"
-#include "journal-authenticate.h"
-#include "journald-rate-limit.h"
+#include "journald-audit.h"
#include "journald-kmsg.h"
-#include "journald-syslog.h"
-#include "journald-stream.h"
#include "journald-native.h"
-#include "journald-audit.h"
+#include "journald-rate-limit.h"
#include "journald-server.h"
+#include "journald-stream.h"
+#include "journald-syslog.h"
+#include "missing.h"
+#include "mkdir.h"
+#include "parse-util.h"
+#include "proc-cmdline.h"
+#include "process-util.h"
+#include "rm-rf.h"
+#include "selinux-util.h"
+#include "signal-util.h"
+#include "socket-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "user-util.h"
#define USER_JOURNALS_MAX 1024
@@ -66,88 +78,63 @@
#define DEFAULT_RATE_LIMIT_BURST 1000
#define DEFAULT_MAX_FILE_USEC USEC_PER_MONTH
-#define RECHECK_AVAILABLE_SPACE_USEC (30*USEC_PER_SEC)
-
-static const char* const storage_table[_STORAGE_MAX] = {
- [STORAGE_AUTO] = "auto",
- [STORAGE_VOLATILE] = "volatile",
- [STORAGE_PERSISTENT] = "persistent",
- [STORAGE_NONE] = "none"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(storage, Storage);
-DEFINE_CONFIG_PARSE_ENUM(config_parse_storage, storage, Storage, "Failed to parse storage setting");
-
-static const char* const split_mode_table[_SPLIT_MAX] = {
- [SPLIT_LOGIN] = "login",
- [SPLIT_UID] = "uid",
- [SPLIT_NONE] = "none",
-};
+#define RECHECK_SPACE_USEC (30*USEC_PER_SEC)
-DEFINE_STRING_TABLE_LOOKUP(split_mode, SplitMode);
-DEFINE_CONFIG_PARSE_ENUM(config_parse_split_mode, split_mode, SplitMode, "Failed to parse split mode setting");
+#define NOTIFY_SNDBUF_SIZE (8*1024*1024)
-static uint64_t available_space(Server *s, bool verbose) {
- char ids[33];
- _cleanup_free_ char *p = NULL;
- sd_id128_t machine;
- struct statvfs ss;
- uint64_t sum = 0, ss_avail = 0, avail = 0;
- int r;
+static int determine_space_for(
+ Server *s,
+ JournalMetrics *metrics,
+ const char *path,
+ const char *name,
+ bool verbose,
+ bool patch_min_use,
+ uint64_t *available,
+ uint64_t *limit) {
+
+ uint64_t sum = 0, ss_avail, avail;
_cleanup_closedir_ DIR *d = NULL;
+ struct dirent *de;
+ struct statvfs ss;
+ const char *p;
usec_t ts;
- const char *f;
- JournalMetrics *m;
-
- ts = now(CLOCK_MONOTONIC);
- if (s->cached_available_space_timestamp + RECHECK_AVAILABLE_SPACE_USEC > ts
- && !verbose)
- return s->cached_available_space;
+ assert(s);
+ assert(metrics);
+ assert(path);
+ assert(name);
- r = sd_id128_get_machine(&machine);
- if (r < 0)
- return 0;
+ ts = now(CLOCK_MONOTONIC);
- if (s->system_journal) {
- f = "/var/log/journal/";
- m = &s->system_metrics;
- } else {
- f = "/run/log/journal/";
- m = &s->runtime_metrics;
- }
+ if (!verbose && s->cached_space_timestamp + RECHECK_SPACE_USEC > ts) {
- assert(m);
+ if (available)
+ *available = s->cached_space_available;
+ if (limit)
+ *limit = s->cached_space_limit;
- p = strappend(f, sd_id128_to_string(machine, ids));
- if (!p)
return 0;
+ }
+ p = strjoina(path, SERVER_MACHINE_ID(s));
d = opendir(p);
if (!d)
- return 0;
+ return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno, "Failed to open %s: %m", p);
if (fstatvfs(dirfd(d), &ss) < 0)
- return 0;
+ return log_error_errno(errno, "Failed to fstatvfs(%s): %m", p);
- for (;;) {
+ FOREACH_DIRENT_ALL(de, d, break) {
struct stat st;
- struct dirent *de;
-
- errno = 0;
- de = readdir(d);
- if (!de && errno != 0)
- return 0;
-
- if (!de)
- break;
if (!endswith(de->d_name, ".journal") &&
!endswith(de->d_name, ".journal~"))
continue;
- if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0)
+ if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
+ log_debug_errno(errno, "Failed to stat %s/%s, ignoring: %m", p, de->d_name);
continue;
+ }
if (!S_ISREG(st.st_mode))
continue;
@@ -155,90 +142,81 @@ static uint64_t available_space(Server *s, bool verbose) {
sum += (uint64_t) st.st_blocks * 512UL;
}
- ss_avail = ss.f_bsize * ss.f_bavail;
-
- /* If we reached a high mark, we will always allow this much
- * again, unless usage goes above max_use. This watermark
- * value is cached so that we don't give up space on pressure,
- * but hover below the maximum usage. */
+ /* If request, then let's bump the min_use limit to the
+ * current usage on disk. We do this when starting up and
+ * first opening the journal files. This way sudden spikes in
+ * disk usage will not cause journald to vacuum files without
+ * bounds. Note that this means that only a restart of
+ * journald will make it reset this value. */
- if (m->use < sum)
- m->use = sum;
+ if (patch_min_use)
+ metrics->min_use = MAX(metrics->min_use, sum);
- avail = LESS_BY(ss_avail, m->keep_free);
+ ss_avail = ss.f_bsize * ss.f_bavail;
+ avail = LESS_BY(ss_avail, metrics->keep_free);
- s->cached_available_space = LESS_BY(MIN(m->max_use, avail), sum);
- s->cached_available_space_timestamp = ts;
+ s->cached_space_limit = MIN(MAX(sum + avail, metrics->min_use), metrics->max_use);
+ s->cached_space_available = LESS_BY(s->cached_space_limit, sum);
+ s->cached_space_timestamp = ts;
if (verbose) {
char fb1[FORMAT_BYTES_MAX], fb2[FORMAT_BYTES_MAX], fb3[FORMAT_BYTES_MAX],
- fb4[FORMAT_BYTES_MAX], fb5[FORMAT_BYTES_MAX];
+ fb4[FORMAT_BYTES_MAX], fb5[FORMAT_BYTES_MAX], fb6[FORMAT_BYTES_MAX];
server_driver_message(s, SD_MESSAGE_JOURNAL_USAGE,
- "%s is currently using %s.\n"
+ "%s (%s) is currently using %s.\n"
"Maximum allowed usage is set to %s.\n"
"Leaving at least %s free (of currently available %s of space).\n"
- "Enforced usage limit is thus %s.",
- s->system_journal ? "Permanent journal (/var/log/journal/)" : "Runtime journal (/run/log/journal/)",
+ "Enforced usage limit is thus %s, of which %s are still available.",
+ name, path,
format_bytes(fb1, sizeof(fb1), sum),
- format_bytes(fb2, sizeof(fb2), m->max_use),
- format_bytes(fb3, sizeof(fb3), m->keep_free),
+ format_bytes(fb2, sizeof(fb2), metrics->max_use),
+ format_bytes(fb3, sizeof(fb3), metrics->keep_free),
format_bytes(fb4, sizeof(fb4), ss_avail),
- format_bytes(fb5, sizeof(fb5), s->cached_available_space + sum));
+ format_bytes(fb5, sizeof(fb5), s->cached_space_limit),
+ format_bytes(fb6, sizeof(fb6), s->cached_space_available));
}
- return s->cached_available_space;
+ if (available)
+ *available = s->cached_space_available;
+ if (limit)
+ *limit = s->cached_space_limit;
+
+ return 1;
}
-void server_fix_perms(Server *s, JournalFile *f, uid_t uid) {
- int r;
+static int determine_space(Server *s, bool verbose, bool patch_min_use, uint64_t *available, uint64_t *limit) {
+ JournalMetrics *metrics;
+ const char *path, *name;
+
+ assert(s);
+
+ if (s->system_journal) {
+ path = "/var/log/journal/";
+ metrics = &s->system_metrics;
+ name = "System journal";
+ } else {
+ path = "/run/log/journal/";
+ metrics = &s->runtime_metrics;
+ name = "Runtime journal";
+ }
+
+ return determine_space_for(s, metrics, path, name, verbose, patch_min_use, available, limit);
+}
+
+static void server_add_acls(JournalFile *f, uid_t uid) {
#ifdef HAVE_ACL
- acl_t acl;
- acl_entry_t entry;
- acl_permset_t permset;
+ int r;
#endif
-
assert(f);
- r = fchmod(f->fd, 0640);
- if (r < 0)
- log_warning_errno(r, "Failed to fix access mode on %s, ignoring: %m", f->path);
-
#ifdef HAVE_ACL
if (uid <= SYSTEM_UID_MAX)
return;
- acl = acl_get_fd(f->fd);
- if (!acl) {
- log_warning_errno(errno, "Failed to read ACL on %s, ignoring: %m", f->path);
- return;
- }
-
- r = acl_find_uid(acl, uid, &entry);
- if (r <= 0) {
-
- if (acl_create_entry(&acl, &entry) < 0 ||
- acl_set_tag_type(entry, ACL_USER) < 0 ||
- acl_set_qualifier(entry, &uid) < 0) {
- log_warning_errno(errno, "Failed to patch ACL on %s, ignoring: %m", f->path);
- goto finish;
- }
- }
-
- /* We do not recalculate the mask unconditionally here,
- * so that the fchmod() mask above stays intact. */
- if (acl_get_permset(entry, &permset) < 0 ||
- acl_add_perm(permset, ACL_READ) < 0 ||
- calc_acl_mask_if_needed(&acl) < 0) {
- log_warning_errno(errno, "Failed to patch ACL on %s, ignoring: %m", f->path);
- goto finish;
- }
-
- if (acl_set_fd(f->fd, acl) < 0)
- log_warning_errno(errno, "Failed to set ACL on %s, ignoring: %m", f->path);
-
-finish:
- acl_free(acl);
+ r = add_acls_for_user(f->fd, uid);
+ if (r < 0)
+ log_warning_errno(r, "Failed to set ACL on %s, ignoring: %m", f->path);
#endif
}
@@ -265,7 +243,7 @@ static JournalFile* find_journal(Server *s, uid_t uid) {
if (r < 0)
return s->system_journal;
- f = ordered_hashmap_get(s->user_journals, UINT32_TO_PTR(uid));
+ f = ordered_hashmap_get(s->user_journals, UID_TO_PTR(uid));
if (f)
return f;
@@ -284,9 +262,9 @@ static JournalFile* find_journal(Server *s, uid_t uid) {
if (r < 0)
return s->system_journal;
- server_fix_perms(s, f, uid);
+ server_add_acls(f, uid);
- r = ordered_hashmap_put(s->user_journals, UINT32_TO_PTR(uid), f);
+ r = ordered_hashmap_put(s->user_journals, UID_TO_PTR(uid), f);
if (r < 0) {
journal_file_close(f);
return s->system_journal;
@@ -315,7 +293,7 @@ static int do_rotate(
else
log_error_errno(r, "Failed to create new %s journal: %m", name);
else
- server_fix_perms(s, *f, uid);
+ server_add_acls(*f, uid);
return r;
}
@@ -328,11 +306,11 @@ void server_rotate(Server *s) {
log_debug("Rotating...");
- do_rotate(s, &s->runtime_journal, "runtime", false, 0);
- do_rotate(s, &s->system_journal, "system", s->seal, 0);
+ (void) do_rotate(s, &s->runtime_journal, "runtime", false, 0);
+ (void) do_rotate(s, &s->system_journal, "system", s->seal, 0);
ORDERED_HASHMAP_FOREACH_KEY(f, k, s->user_journals, i) {
- r = do_rotate(s, &f, "user", s->seal, PTR_TO_UINT32(k));
+ r = do_rotate(s, &f, "user", s->seal, PTR_TO_UID(k));
if (r >= 0)
ordered_hashmap_replace(s->user_journals, k, f);
else if (!f)
@@ -343,20 +321,19 @@ void server_rotate(Server *s) {
void server_sync(Server *s) {
JournalFile *f;
- void *k;
Iterator i;
int r;
if (s->system_journal) {
r = journal_file_set_offline(s->system_journal);
if (r < 0)
- log_error_errno(r, "Failed to sync system journal: %m");
+ log_warning_errno(r, "Failed to sync system journal, ignoring: %m");
}
- ORDERED_HASHMAP_FOREACH_KEY(f, k, s->user_journals, i) {
+ ORDERED_HASHMAP_FOREACH(f, s->user_journals, i) {
r = journal_file_set_offline(f);
if (r < 0)
- log_error_errno(r, "Failed to sync user journal: %m");
+ log_warning_errno(r, "Failed to sync user journal, ignoring: %m");
}
if (s->sync_event_source) {
@@ -370,43 +347,50 @@ void server_sync(Server *s) {
static void do_vacuum(
Server *s,
- const char *id,
JournalFile *f,
- const char* path,
- JournalMetrics *metrics) {
+ JournalMetrics *metrics,
+ const char *path,
+ const char *name,
+ bool verbose,
+ bool patch_min_use) {
const char *p;
+ uint64_t limit;
int r;
+ assert(s);
+ assert(metrics);
+ assert(path);
+ assert(name);
+
if (!f)
return;
- p = strjoina(path, id);
- r = journal_directory_vacuum(p, metrics->max_use, s->max_retention_usec, &s->oldest_file_usec, false);
+ p = strjoina(path, SERVER_MACHINE_ID(s));
+
+ limit = metrics->max_use;
+ (void) determine_space_for(s, metrics, path, name, verbose, patch_min_use, NULL, &limit);
+
+ r = journal_directory_vacuum(p, limit, metrics->n_max_files, s->max_retention_usec, &s->oldest_file_usec, verbose);
if (r < 0 && r != -ENOENT)
- log_error_errno(r, "Failed to vacuum %s: %m", p);
+ log_warning_errno(r, "Failed to vacuum %s, ignoring: %m", p);
}
-void server_vacuum(Server *s) {
- char ids[33];
- sd_id128_t machine;
- int r;
+int server_vacuum(Server *s, bool verbose, bool patch_min_use) {
+ assert(s);
log_debug("Vacuuming...");
s->oldest_file_usec = 0;
- r = sd_id128_get_machine(&machine);
- if (r < 0) {
- log_error_errno(r, "Failed to get machine ID: %m");
- return;
- }
- sd_id128_to_string(machine, ids);
+ do_vacuum(s, s->system_journal, &s->system_metrics, "/var/log/journal/", "System journal", verbose, patch_min_use);
+ do_vacuum(s, s->runtime_journal, &s->runtime_metrics, "/run/log/journal/", "Runtime journal", verbose, patch_min_use);
- do_vacuum(s, ids, s->system_journal, "/var/log/journal/", &s->system_metrics);
- do_vacuum(s, ids, s->runtime_journal, "/run/log/journal/", &s->runtime_metrics);
+ s->cached_space_limit = 0;
+ s->cached_space_available = 0;
+ s->cached_space_timestamp = 0;
- s->cached_available_space_timestamp = 0;
+ return 0;
}
static void server_cache_machine_id(Server *s) {
@@ -504,7 +488,7 @@ static void write_to_journal(Server *s, uid_t uid, struct iovec *iovec, unsigned
if (journal_file_rotate_suggested(f, s->max_file_usec)) {
log_debug("%s: Journal header limits reached or header out-of-date, rotating.", f->path);
server_rotate(s);
- server_vacuum(s);
+ server_vacuum(s, false, false);
vacuumed = true;
f = find_journal(s, uid);
@@ -524,7 +508,7 @@ static void write_to_journal(Server *s, uid_t uid, struct iovec *iovec, unsigned
}
server_rotate(s);
- server_vacuum(s);
+ server_vacuum(s, false, false);
f = find_journal(s, uid);
if (!f)
@@ -684,7 +668,7 @@ static void dispatch_message_real(
}
#ifdef HAVE_SELINUX
- if (mac_selinux_use()) {
+ if (mac_selinux_have()) {
if (label) {
x = alloca(strlen("_SELINUX_CONTEXT=") + label_len + 1);
@@ -825,7 +809,7 @@ static void dispatch_message_real(
void server_driver_message(Server *s, sd_id128_t message_id, const char *format, ...) {
char mid[11 + 32 + 1];
char buffer[16 + LINE_MAX + 1];
- struct iovec iovec[N_IOVEC_META_FIELDS + 4];
+ struct iovec iovec[N_IOVEC_META_FIELDS + 6];
int n = 0;
va_list ap;
struct ucred ucred = {};
@@ -833,6 +817,9 @@ void server_driver_message(Server *s, sd_id128_t message_id, const char *format,
assert(s);
assert(format);
+ IOVEC_SET_STRING(iovec[n++], "SYSLOG_FACILITY=3");
+ IOVEC_SET_STRING(iovec[n++], "SYSLOG_IDENTIFIER=systemd-journald");
+
IOVEC_SET_STRING(iovec[n++], "PRIORITY=6");
IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=driver");
@@ -866,6 +853,7 @@ void server_dispatch_message(
int rl, r;
_cleanup_free_ char *path = NULL;
+ uint64_t available = 0;
char *c;
assert(s);
@@ -905,9 +893,8 @@ void server_dispatch_message(
}
}
- rl = journal_rate_limit_test(s->rate_limit, path,
- priority & LOG_PRIMASK, available_space(s, false));
-
+ (void) determine_space(s, false, false, &available, NULL);
+ rl = journal_rate_limit_test(s->rate_limit, path, priority & LOG_PRIMASK, available);
if (rl == 0)
return;
@@ -922,16 +909,8 @@ finish:
static int system_journal_open(Server *s, bool flush_requested) {
- int r;
- char *fn;
- sd_id128_t machine;
- char ids[33];
-
- r = sd_id128_get_machine(&machine);
- if (r < 0)
- return log_error_errno(r, "Failed to get machine id: %m");
-
- sd_id128_to_string(machine, ids);
+ const char *fn;
+ int r = 0;
if (!s->system_journal &&
(s->storage == STORAGE_PERSISTENT || s->storage == STORAGE_AUTO) &&
@@ -947,15 +926,15 @@ static int system_journal_open(Server *s, bool flush_requested) {
if (s->storage == STORAGE_PERSISTENT)
(void) mkdir_p("/var/log/journal/", 0755);
- fn = strjoina("/var/log/journal/", ids);
+ fn = strjoina("/var/log/journal/", SERVER_MACHINE_ID(s));
(void) mkdir(fn, 0755);
fn = strjoina(fn, "/system.journal");
r = journal_file_open_reliably(fn, O_RDWR|O_CREAT, 0640, s->compress, s->seal, &s->system_metrics, s->mmap, NULL, &s->system_journal);
-
- if (r >= 0)
- server_fix_perms(s, s->system_journal, 0);
- else if (r < 0) {
+ if (r >= 0) {
+ server_add_acls(s->system_journal, 0);
+ (void) determine_space_for(s, &s->system_metrics, "/var/log/journal/", "System journal", true, true, NULL, NULL);
+ } else if (r < 0) {
if (r != -ENOENT && r != -EROFS)
log_warning_errno(r, "Failed to open system journal: %m");
@@ -966,9 +945,7 @@ static int system_journal_open(Server *s, bool flush_requested) {
if (!s->runtime_journal &&
(s->storage != STORAGE_NONE)) {
- fn = strjoin("/run/log/journal/", ids, "/system.journal", NULL);
- if (!fn)
- return -ENOMEM;
+ fn = strjoina("/run/log/journal/", SERVER_MACHINE_ID(s), "/system.journal");
if (s->system_journal) {
@@ -977,8 +954,6 @@ static int system_journal_open(Server *s, bool flush_requested) {
* it into the system journal */
r = journal_file_open(fn, O_RDWR, 0640, s->compress, false, &s->runtime_metrics, s->mmap, NULL, &s->runtime_journal);
- free(fn);
-
if (r < 0) {
if (r != -ENOENT)
log_warning_errno(r, "Failed to open runtime journal: %m");
@@ -996,18 +971,16 @@ static int system_journal_open(Server *s, bool flush_requested) {
(void) mkdir_parents(fn, 0750);
r = journal_file_open_reliably(fn, O_RDWR|O_CREAT, 0640, s->compress, false, &s->runtime_metrics, s->mmap, NULL, &s->runtime_journal);
- free(fn);
-
if (r < 0)
return log_error_errno(r, "Failed to open runtime journal: %m");
}
- if (s->runtime_journal)
- server_fix_perms(s, s->runtime_journal, 0);
+ if (s->runtime_journal) {
+ server_add_acls(s->runtime_journal, 0);
+ (void) determine_space_for(s, &s->runtime_metrics, "/run/log/journal/", "Runtime journal", true, true, NULL, NULL);
+ }
}
- available_space(s, true);
-
return r;
}
@@ -1028,7 +1001,7 @@ int server_flush_to_var(Server *s) {
if (!s->runtime_journal)
return 0;
- system_journal_open(s, true);
+ (void) system_journal_open(s, true);
if (!s->system_journal)
return 0;
@@ -1072,7 +1045,7 @@ int server_flush_to_var(Server *s) {
}
server_rotate(s);
- server_vacuum(s);
+ server_vacuum(s, false, false);
if (!s->system_journal) {
log_notice("Didn't flush runtime journal since rotation of system journal wasn't successful.");
@@ -1088,11 +1061,12 @@ int server_flush_to_var(Server *s) {
}
}
+ r = 0;
+
finish:
journal_file_post_change(s->system_journal);
- journal_file_close(s->runtime_journal);
- s->runtime_journal = NULL;
+ s->runtime_journal = journal_file_close(s->runtime_journal);
if (r >= 0)
(void) rm_rf("/run/log/journal", REMOVE_ROOT);
@@ -1228,28 +1202,37 @@ int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void
static int dispatch_sigusr1(sd_event_source *es, const struct signalfd_siginfo *si, void *userdata) {
Server *s = userdata;
+ int r;
assert(s);
- log_info("Received request to flush runtime journal from PID %"PRIu32, si->ssi_pid);
+ log_info("Received request to flush runtime journal from PID " PID_FMT, si->ssi_pid);
server_flush_to_var(s);
server_sync(s);
- server_vacuum(s);
+ server_vacuum(s, false, false);
- touch("/run/systemd/journal/flushed");
+ r = touch("/run/systemd/journal/flushed");
+ if (r < 0)
+ log_warning_errno(r, "Failed to touch /run/systemd/journal/flushed, ignoring: %m");
return 0;
}
static int dispatch_sigusr2(sd_event_source *es, const struct signalfd_siginfo *si, void *userdata) {
Server *s = userdata;
+ int r;
assert(s);
- log_info("Received request to rotate journal from PID %"PRIu32, si->ssi_pid);
+ log_info("Received request to rotate journal from PID " PID_FMT, si->ssi_pid);
server_rotate(s);
- server_vacuum(s);
+ server_vacuum(s, true, true);
+
+ /* Let clients know when the most recent rotation happened. */
+ r = write_timestamp_file_atomic("/run/systemd/journal/rotated", now(CLOCK_MONOTONIC));
+ if (r < 0)
+ log_warning_errno(r, "Failed to write /run/systemd/journal/rotated, ignoring: %m");
return 0;
}
@@ -1265,12 +1248,30 @@ static int dispatch_sigterm(sd_event_source *es, const struct signalfd_siginfo *
return 0;
}
+static int dispatch_sigrtmin1(sd_event_source *es, const struct signalfd_siginfo *si, void *userdata) {
+ Server *s = userdata;
+ int r;
+
+ assert(s);
+
+ log_debug("Received request to sync from PID " PID_FMT, si->ssi_pid);
+
+ server_sync(s);
+
+ /* Let clients know when the most recent sync happened. */
+ r = write_timestamp_file_atomic("/run/systemd/journal/synced", now(CLOCK_MONOTONIC));
+ if (r < 0)
+ log_warning_errno(r, "Failed to write /run/systemd/journal/synced, ignoring: %m");
+
+ return 0;
+}
+
static int setup_signals(Server *s) {
int r;
assert(s);
- assert(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, SIGTERM, SIGUSR1, SIGUSR2, -1) >= 0);
+ assert(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, SIGTERM, SIGUSR1, SIGUSR2, SIGRTMIN+1, -1) >= 0);
r = sd_event_add_signal(s->event, &s->sigusr1_event_source, SIGUSR1, dispatch_sigusr1, s);
if (r < 0)
@@ -1284,17 +1285,41 @@ static int setup_signals(Server *s) {
if (r < 0)
return r;
+ /* Let's process SIGTERM late, so that we flush all queued
+ * messages to disk before we exit */
+ r = sd_event_source_set_priority(s->sigterm_event_source, SD_EVENT_PRIORITY_NORMAL+20);
+ if (r < 0)
+ return r;
+
+ /* When journald is invoked on the terminal (when debugging),
+ * it's useful if C-c is handled equivalent to SIGTERM. */
r = sd_event_add_signal(s->event, &s->sigint_event_source, SIGINT, dispatch_sigterm, s);
if (r < 0)
return r;
+ r = sd_event_source_set_priority(s->sigint_event_source, SD_EVENT_PRIORITY_NORMAL+20);
+ if (r < 0)
+ return r;
+
+ /* SIGRTMIN+1 causes an immediate sync. We process this very
+ * late, so that everything else queued at this point is
+ * really written to disk. Clients can watch
+ * /run/systemd/journal/synced with inotify until its mtime
+ * changes to see when a sync happened. */
+ r = sd_event_add_signal(s->event, &s->sigrtmin1_event_source, SIGRTMIN+1, dispatch_sigrtmin1, s);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_priority(s->sigrtmin1_event_source, SD_EVENT_PRIORITY_NORMAL+15);
+ if (r < 0)
+ return r;
+
return 0;
}
static int server_parse_proc_cmdline(Server *s) {
_cleanup_free_ char *line = NULL;
- const char *w, *state;
- size_t l;
+ const char *p;
int r;
r = proc_cmdline(&line);
@@ -1303,12 +1328,16 @@ static int server_parse_proc_cmdline(Server *s) {
return 0;
}
- FOREACH_WORD_QUOTED(w, l, line, state) {
- _cleanup_free_ char *word;
+ p = line;
+ for(;;) {
+ _cleanup_free_ char *word = NULL;
- word = strndup(w, l);
- if (!word)
- return -ENOMEM;
+ r = extract_first_word(&p, &word, NULL, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse journald syntax \"%s\": %m", line);
+
+ if (r == 0)
+ break;
if (startswith(word, "systemd.journald.forward_to_syslog=")) {
r = parse_boolean(word + 35);
@@ -1337,16 +1366,16 @@ static int server_parse_proc_cmdline(Server *s) {
} else if (startswith(word, "systemd.journald"))
log_warning("Invalid systemd.journald parameter. Ignoring.");
}
- /* do not warn about state here, since probably systemd already did */
+ /* do not warn about state here, since probably systemd already did */
return 0;
}
static int server_parse_config_file(Server *s) {
assert(s);
- return config_parse_many("/etc/systemd/journald.conf",
- CONF_DIRS_NULSTR("systemd/journald.conf"),
+ return config_parse_many(PKGSYSCONFDIR "/journald.conf",
+ CONF_PATHS_NULSTR("systemd/journald.conf.d"),
"Journal\0",
config_item_perf_lookup, journald_gperf_lookup,
false, s);
@@ -1434,8 +1463,7 @@ static int server_open_hostname(Server *s) {
/* kernels prior to 3.2 don't support polling this file. Ignore
* the failure. */
if (r == -EPERM) {
- log_warning("Failed to register hostname fd in event loop: %s. Ignoring.",
- strerror(-r));
+ log_warning_errno(r, "Failed to register hostname fd in event loop, ignoring: %m");
s->hostname_fd = safe_close(s->hostname_fd);
return 0;
}
@@ -1450,17 +1478,184 @@ static int server_open_hostname(Server *s) {
return 0;
}
+static int dispatch_notify_event(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
+ Server *s = userdata;
+ int r;
+
+ assert(s);
+ assert(s->notify_event_source == es);
+ assert(s->notify_fd == fd);
+
+ /* The $NOTIFY_SOCKET is writable again, now send exactly one
+ * message on it. Either it's the wtachdog event, the initial
+ * READY=1 event or an stdout stream event. If there's nothing
+ * to write anymore, turn our event source off. The next time
+ * there's something to send it will be turned on again. */
+
+ if (!s->sent_notify_ready) {
+ static const char p[] =
+ "READY=1\n"
+ "STATUS=Processing requests...";
+ ssize_t l;
+
+ l = send(s->notify_fd, p, strlen(p), MSG_DONTWAIT);
+ if (l < 0) {
+ if (errno == EAGAIN)
+ return 0;
+
+ return log_error_errno(errno, "Failed to send READY=1 notification message: %m");
+ }
+
+ s->sent_notify_ready = true;
+ log_debug("Sent READY=1 notification.");
+
+ } else if (s->send_watchdog) {
+
+ static const char p[] =
+ "WATCHDOG=1";
+
+ ssize_t l;
+
+ l = send(s->notify_fd, p, strlen(p), MSG_DONTWAIT);
+ if (l < 0) {
+ if (errno == EAGAIN)
+ return 0;
+
+ return log_error_errno(errno, "Failed to send WATCHDOG=1 notification message: %m");
+ }
+
+ s->send_watchdog = false;
+ log_debug("Sent WATCHDOG=1 notification.");
+
+ } else if (s->stdout_streams_notify_queue)
+ /* Dispatch one stream notification event */
+ stdout_stream_send_notify(s->stdout_streams_notify_queue);
+
+ /* Leave us enabled if there's still more to to do. */
+ if (s->send_watchdog || s->stdout_streams_notify_queue)
+ return 0;
+
+ /* There was nothing to do anymore, let's turn ourselves off. */
+ r = sd_event_source_set_enabled(es, SD_EVENT_OFF);
+ if (r < 0)
+ return log_error_errno(r, "Failed to turn off notify event source: %m");
+
+ return 0;
+}
+
+static int dispatch_watchdog(sd_event_source *es, uint64_t usec, void *userdata) {
+ Server *s = userdata;
+ int r;
+
+ assert(s);
+
+ s->send_watchdog = true;
+
+ r = sd_event_source_set_enabled(s->notify_event_source, SD_EVENT_ON);
+ if (r < 0)
+ log_warning_errno(r, "Failed to turn on notify event source: %m");
+
+ r = sd_event_source_set_time(s->watchdog_event_source, usec + s->watchdog_usec / 2);
+ if (r < 0)
+ return log_error_errno(r, "Failed to restart watchdog event source: %m");
+
+ r = sd_event_source_set_enabled(s->watchdog_event_source, SD_EVENT_ON);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enable watchdog event source: %m");
+
+ return 0;
+}
+
+static int server_connect_notify(Server *s) {
+ union sockaddr_union sa = {
+ .un.sun_family = AF_UNIX,
+ };
+ const char *e;
+ int r;
+
+ assert(s);
+ assert(s->notify_fd < 0);
+ assert(!s->notify_event_source);
+
+ /*
+ So here's the problem: we'd like to send notification
+ messages to PID 1, but we cannot do that via sd_notify(),
+ since that's synchronous, and we might end up blocking on
+ it. Specifically: given that PID 1 might block on
+ dbus-daemon during IPC, and dbus-daemon is logging to us,
+ and might hence block on us, we might end up in a deadlock
+ if we block on sending PID 1 notification messages -- by
+ generating a full blocking circle. To avoid this, let's
+ create a non-blocking socket, and connect it to the
+ notification socket, and then wait for POLLOUT before we
+ send anything. This should efficiently avoid any deadlocks,
+ as we'll never block on PID 1, hence PID 1 can safely block
+ on dbus-daemon which can safely block on us again.
+
+ Don't think that this issue is real? It is, see:
+ https://github.com/systemd/systemd/issues/1505
+ */
+
+ e = getenv("NOTIFY_SOCKET");
+ if (!e)
+ return 0;
+
+ if ((e[0] != '@' && e[0] != '/') || e[1] == 0) {
+ log_error("NOTIFY_SOCKET set to an invalid value: %s", e);
+ return -EINVAL;
+ }
+
+ if (strlen(e) > sizeof(sa.un.sun_path)) {
+ log_error("NOTIFY_SOCKET path too long: %s", e);
+ return -EINVAL;
+ }
+
+ s->notify_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (s->notify_fd < 0)
+ return log_error_errno(errno, "Failed to create notify socket: %m");
+
+ (void) fd_inc_sndbuf(s->notify_fd, NOTIFY_SNDBUF_SIZE);
+
+ strncpy(sa.un.sun_path, e, sizeof(sa.un.sun_path));
+ if (sa.un.sun_path[0] == '@')
+ sa.un.sun_path[0] = 0;
+
+ r = connect(s->notify_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(e));
+ if (r < 0)
+ return log_error_errno(errno, "Failed to connect to notify socket: %m");
+
+ r = sd_event_add_io(s->event, &s->notify_event_source, s->notify_fd, EPOLLOUT, dispatch_notify_event, s);
+ if (r < 0)
+ return log_error_errno(r, "Failed to watch notification socket: %m");
+
+ if (sd_watchdog_enabled(false, &s->watchdog_usec) > 0) {
+ s->send_watchdog = true;
+
+ r = sd_event_add_time(s->event, &s->watchdog_event_source, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + s->watchdog_usec/2, s->watchdog_usec/4, dispatch_watchdog, s);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add watchdog time event: %m");
+ }
+
+ /* This should fire pretty soon, which we'll use to send the
+ * READY=1 event. */
+
+ return 0;
+}
+
int server_init(Server *s) {
_cleanup_fdset_free_ FDSet *fds = NULL;
int n, r, fd;
+ bool no_sockets;
assert(s);
zero(*s);
- s->syslog_fd = s->native_fd = s->stdout_fd = s->dev_kmsg_fd = s->audit_fd = s->hostname_fd = -1;
+ s->syslog_fd = s->native_fd = s->stdout_fd = s->dev_kmsg_fd = s->audit_fd = s->hostname_fd = s->notify_fd = -1;
s->compress = true;
s->seal = true;
+ s->watchdog_usec = USEC_INFINITY;
+
s->sync_interval_usec = DEFAULT_SYNC_INTERVAL_USEC;
s->sync_scheduled = false;
@@ -1477,18 +1672,19 @@ int server_init(Server *s) {
s->max_level_console = LOG_INFO;
s->max_level_wall = LOG_EMERG;
- memset(&s->system_metrics, 0xFF, sizeof(s->system_metrics));
- memset(&s->runtime_metrics, 0xFF, sizeof(s->runtime_metrics));
+ journal_reset_metrics(&s->system_metrics);
+ journal_reset_metrics(&s->runtime_metrics);
server_parse_config_file(s);
server_parse_proc_cmdline(s);
+
if (!!s->rate_limit_interval ^ !!s->rate_limit_burst) {
log_debug("Setting both rate limit interval and burst from "USEC_FMT",%u to 0,0",
s->rate_limit_interval, s->rate_limit_burst);
s->rate_limit_interval = s->rate_limit_burst = 0;
}
- mkdir_p("/run/systemd/journal", 0755);
+ (void) mkdir_p("/run/systemd/journal", 0755);
s->user_journals = ordered_hashmap_new(NULL);
if (!s->user_journals)
@@ -1502,8 +1698,6 @@ int server_init(Server *s) {
if (r < 0)
return log_error_errno(r, "Failed to create event loop: %m");
- sd_event_set_watchdog(s->event, true);
-
n = sd_listen_fds(true);
if (n < 0)
return log_error_errno(n, "Failed to read listening file descriptors from environment: %m");
@@ -1561,30 +1755,44 @@ int server_init(Server *s) {
}
}
- r = server_open_stdout_socket(s, fds);
- if (r < 0)
- return r;
+ /* Try to restore streams, but don't bother if this fails */
+ (void) server_restore_streams(s, fds);
if (fdset_size(fds) > 0) {
log_warning("%u unknown file descriptors passed, closing.", fdset_size(fds));
fds = fdset_free(fds);
}
+ no_sockets = s->native_fd < 0 && s->stdout_fd < 0 && s->syslog_fd < 0 && s->audit_fd < 0;
+
+ /* always open stdout, syslog, native, and kmsg sockets */
+
+ /* systemd-journald.socket: /run/systemd/journal/stdout */
+ r = server_open_stdout_socket(s);
+ if (r < 0)
+ return r;
+
+ /* systemd-journald-dev-log.socket: /run/systemd/journal/dev-log */
r = server_open_syslog_socket(s);
if (r < 0)
return r;
+ /* systemd-journald.socket: /run/systemd/journal/socket */
r = server_open_native_socket(s);
if (r < 0)
return r;
+ /* /dev/ksmg */
r = server_open_dev_kmsg(s);
if (r < 0)
return r;
- r = server_open_audit(s);
- if (r < 0)
- return r;
+ /* Unless we got *some* sockets and not audit, open audit socket */
+ if (s->audit_fd >= 0 || no_sockets) {
+ r = server_open_audit(s);
+ if (r < 0)
+ return r;
+ }
r = server_open_kernel_seqnum(s);
if (r < 0)
@@ -1614,11 +1822,9 @@ int server_init(Server *s) {
server_cache_boot_id(s);
server_cache_machine_id(s);
- r = system_journal_open(s, false);
- if (r < 0)
- return r;
+ (void) server_connect_notify(s);
- return 0;
+ return system_journal_open(s, false);
}
void server_maybe_append_tags(Server *s) {
@@ -1665,7 +1871,10 @@ void server_done(Server *s) {
sd_event_source_unref(s->sigusr2_event_source);
sd_event_source_unref(s->sigterm_event_source);
sd_event_source_unref(s->sigint_event_source);
+ sd_event_source_unref(s->sigrtmin1_event_source);
sd_event_source_unref(s->hostname_event_source);
+ sd_event_source_unref(s->notify_event_source);
+ sd_event_source_unref(s->watchdog_event_source);
sd_event_unref(s->event);
safe_close(s->syslog_fd);
@@ -1674,6 +1883,7 @@ void server_done(Server *s) {
safe_close(s->dev_kmsg_fd);
safe_close(s->audit_fd);
safe_close(s->hostname_fd);
+ safe_close(s->notify_fd);
if (s->rate_limit)
journal_rate_limit_free(s->rate_limit);
@@ -1691,3 +1901,22 @@ void server_done(Server *s) {
udev_unref(s->udev);
}
+
+static const char* const storage_table[_STORAGE_MAX] = {
+ [STORAGE_AUTO] = "auto",
+ [STORAGE_VOLATILE] = "volatile",
+ [STORAGE_PERSISTENT] = "persistent",
+ [STORAGE_NONE] = "none"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(storage, Storage);
+DEFINE_CONFIG_PARSE_ENUM(config_parse_storage, storage, Storage, "Failed to parse storage setting");
+
+static const char* const split_mode_table[_SPLIT_MAX] = {
+ [SPLIT_LOGIN] = "login",
+ [SPLIT_UID] = "uid",
+ [SPLIT_NONE] = "none",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(split_mode, SplitMode);
+DEFINE_CONFIG_PARSE_ENUM(config_parse_split_mode, split_mode, SplitMode, "Failed to parse split mode setting");
diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h
index d954c5190d..1822765228 100644
--- a/src/journal/journald-server.h
+++ b/src/journal/journald-server.h
@@ -25,10 +25,13 @@
#include <sys/types.h>
#include "sd-event.h"
-#include "journal-file.h"
+
+typedef struct Server Server;
+
#include "hashmap.h"
-#include "audit.h"
+#include "journal-file.h"
#include "journald-rate-limit.h"
+#include "journald-stream.h"
#include "list.h"
typedef enum Storage {
@@ -48,15 +51,14 @@ typedef enum SplitMode {
_SPLIT_INVALID = -1
} SplitMode;
-typedef struct StdoutStream StdoutStream;
-
-typedef struct Server {
+struct Server {
int syslog_fd;
int native_fd;
int stdout_fd;
int dev_kmsg_fd;
int audit_fd;
int hostname_fd;
+ int notify_fd;
sd_event *event;
@@ -70,7 +72,10 @@ typedef struct Server {
sd_event_source *sigusr2_event_source;
sd_event_source *sigterm_event_source;
sd_event_source *sigint_event_source;
+ sd_event_source *sigrtmin1_event_source;
sd_event_source *hostname_event_source;
+ sd_event_source *notify_event_source;
+ sd_event_source *watchdog_event_source;
JournalFile *runtime_journal;
JournalFile *system_journal;
@@ -100,8 +105,9 @@ typedef struct Server {
unsigned n_forward_syslog_missed;
usec_t last_warn_forward_syslog_missed;
- uint64_t cached_available_space;
- usec_t cached_available_space_timestamp;
+ uint64_t cached_space_available;
+ uint64_t cached_space_limit;
+ usec_t cached_space_timestamp;
uint64_t var_available_timestamp;
@@ -110,6 +116,7 @@ typedef struct Server {
usec_t oldest_file_usec;
LIST_HEAD(StdoutStream, stdout_streams);
+ LIST_HEAD(StdoutStream, stdout_streams_notify_queue);
unsigned n_stdout_streams;
char *tty_path;
@@ -125,13 +132,14 @@ typedef struct Server {
MMapCache *mmap;
- bool dev_kmsg_readable;
+ struct udev *udev;
uint64_t *kernel_seqnum;
+ bool dev_kmsg_readable:1;
- struct udev *udev;
-
- bool sync_scheduled;
+ bool send_watchdog:1;
+ bool sent_notify_ready:1;
+ bool sync_scheduled:1;
char machine_id_field[sizeof("_MACHINE_ID=") + 32];
char boot_id_field[sizeof("_BOOT_ID=") + 32];
@@ -139,7 +147,11 @@ typedef struct Server {
/* Cached cgroup root, so that we don't have to query that all the time */
char *cgroup_root;
-} Server;
+
+ usec_t watchdog_usec;
+};
+
+#define SERVER_MACHINE_ID(s) ((s)->machine_id_field + strlen("_MACHINE_ID="))
#define N_IOVEC_META_FIELDS 20
#define N_IOVEC_KERNEL_FIELDS 64
@@ -162,11 +174,10 @@ int config_parse_split_mode(const char *unit, const char *filename, unsigned lin
const char *split_mode_to_string(SplitMode s) _const_;
SplitMode split_mode_from_string(const char *s) _pure_;
-void server_fix_perms(Server *s, JournalFile *f, uid_t uid);
int server_init(Server *s);
void server_done(Server *s);
void server_sync(Server *s);
-void server_vacuum(Server *s);
+int server_vacuum(Server *s, bool verbose, bool patch_min_use);
void server_rotate(Server *s);
int server_schedule_sync(Server *s, int priority);
int server_flush_to_var(Server *s);
diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c
index 69e2d41863..90884b6929 100644
--- a/src/journal/journald-stream.c
+++ b/src/journal/journald-stream.c
@@ -19,25 +19,35 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
#include <stddef.h>
+#include <unistd.h>
#ifdef HAVE_SELINUX
#include <selinux/selinux.h>
#endif
-#include "sd-event.h"
#include "sd-daemon.h"
-#include "socket-util.h"
-#include "selinux-util.h"
-#include "mkdir.h"
+#include "sd-event.h"
+
+#include "alloc-util.h"
+#include "dirent-util.h"
+#include "escape.h"
+#include "fd-util.h"
#include "fileio.h"
+#include "io-util.h"
+#include "journald-console.h"
+#include "journald-kmsg.h"
#include "journald-server.h"
#include "journald-stream.h"
#include "journald-syslog.h"
-#include "journald-kmsg.h"
-#include "journald-console.h"
#include "journald-wall.h"
+#include "mkdir.h"
+#include "parse-util.h"
+#include "selinux-util.h"
+#include "socket-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
+#include "syslog-util.h"
#define STDOUT_STREAMS_MAX 4096
@@ -69,6 +79,7 @@ struct StdoutStream {
bool forward_to_console:1;
bool fdstore:1;
+ bool in_notify_queue:1;
char buffer[LINE_MAX+1];
size_t length;
@@ -78,6 +89,7 @@ struct StdoutStream {
char *state_file;
LIST_FIELDS(StdoutStream, stdout_stream);
+ LIST_FIELDS(StdoutStream, stdout_stream_notify_queue);
};
void stdout_stream_free(StdoutStream *s) {
@@ -88,6 +100,9 @@ void stdout_stream_free(StdoutStream *s) {
assert(s->server->n_stdout_streams > 0);
s->server->n_stdout_streams --;
LIST_REMOVE(stdout_stream, s->server->stdout_streams, s);
+
+ if (s->in_notify_queue)
+ LIST_REMOVE(stdout_stream_notify_queue, s->server->stdout_streams_notify_queue, s);
}
if (s->event_source) {
@@ -111,7 +126,7 @@ static void stdout_stream_destroy(StdoutStream *s) {
return;
if (s->state_file)
- unlink(s->state_file);
+ (void) unlink(s->state_file);
stdout_stream_free(s);
}
@@ -190,11 +205,15 @@ static int stdout_stream_save(StdoutStream *s) {
goto fail;
}
- /* Store the connection fd in PID 1, so that we get it passed
- * in again on next start */
- if (!s->fdstore) {
- sd_pid_notify_with_fds(0, false, "FDSTORE=1", &s->fd, 1);
- s->fdstore = true;
+ if (!s->fdstore && !s->in_notify_queue) {
+ LIST_PREPEND(stdout_stream_notify_queue, s->server->stdout_streams_notify_queue, s);
+ s->in_notify_queue = true;
+
+ if (s->server->notify_event_source) {
+ r = sd_event_source_set_enabled(s->server->notify_event_source, SD_EVENT_ON);
+ if (r < 0)
+ log_warning_errno(r, "Failed to enable notify event source: %m");
+ }
}
return 0;
@@ -220,14 +239,14 @@ static int stdout_stream_log(StdoutStream *s, const char *p) {
assert(s);
assert(p);
- if (isempty(p))
- return 0;
-
priority = s->priority;
if (s->level_prefix)
syslog_parse_priority(&p, &priority, false);
+ if (isempty(p))
+ return 0;
+
if (s->forward_to_syslog || s->server->forward_to_syslog)
server_forward_syslog(s->server, syslog_fixup_facility(priority), s->identifier, p, &s->ucred, NULL);
@@ -267,10 +286,12 @@ static int stdout_stream_log(StdoutStream *s, const char *p) {
static int stdout_stream_line(StdoutStream *s, char *p) {
int r;
+ char *orig;
assert(s);
assert(p);
+ orig = p;
p = strstrip(p);
switch (s->state) {
@@ -359,7 +380,7 @@ static int stdout_stream_line(StdoutStream *s, char *p) {
return 0;
case STDOUT_STREAM_RUNNING:
- return stdout_stream_log(s, p);
+ return stdout_stream_log(s, orig);
}
assert_not_reached("Unknown stream state");
@@ -472,7 +493,7 @@ static int stdout_stream_install(Server *s, int fd, StdoutStream **ret) {
if (r < 0)
return log_error_errno(r, "Failed to determine peer credentials: %m");
- if (mac_selinux_use()) {
+ if (mac_selinux_have()) {
r = getpeersec(fd, &stream->label);
if (r < 0 && r != -EOPNOTSUPP)
(void) log_warning_errno(r, "Failed to determine peer security context: %m");
@@ -519,8 +540,7 @@ static int stdout_stream_new(sd_event_source *es, int listen_fd, uint32_t revent
if (errno == EAGAIN)
return 0;
- log_error_errno(errno, "Failed to accept stdout connection: %m");
- return -errno;
+ return log_error_errno(errno, "Failed to accept stdout connection: %m");
}
if (s->n_stdout_streams >= STDOUT_STREAMS_MAX) {
@@ -627,7 +647,7 @@ static int stdout_stream_restore(Server *s, const char *fname, int fd) {
return 0;
}
-static int server_restore_streams(Server *s, FDSet *fds) {
+int server_restore_streams(Server *s, FDSet *fds) {
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
int r;
@@ -681,7 +701,7 @@ fail:
return log_error_errno(errno, "Failed to read streams directory: %m");
}
-int server_open_stdout_socket(Server *s, FDSet *fds) {
+int server_open_stdout_socket(Server *s) {
int r;
assert(s);
@@ -713,12 +733,56 @@ int server_open_stdout_socket(Server *s, FDSet *fds) {
if (r < 0)
return log_error_errno(r, "Failed to add stdout server fd to event source: %m");
- r = sd_event_source_set_priority(s->stdout_event_source, SD_EVENT_PRIORITY_NORMAL+10);
+ r = sd_event_source_set_priority(s->stdout_event_source, SD_EVENT_PRIORITY_NORMAL+5);
if (r < 0)
return log_error_errno(r, "Failed to adjust priority of stdout server event source: %m");
- /* Try to restore streams, but don't bother if this fails */
- (void) server_restore_streams(s, fds);
-
return 0;
}
+
+void stdout_stream_send_notify(StdoutStream *s) {
+ struct iovec iovec = {
+ .iov_base = (char*) "FDSTORE=1",
+ .iov_len = strlen("FDSTORE=1"),
+ };
+ struct msghdr msghdr = {
+ .msg_iov = &iovec,
+ .msg_iovlen = 1,
+ };
+ struct cmsghdr *cmsg;
+ ssize_t l;
+
+ assert(s);
+ assert(!s->fdstore);
+ assert(s->in_notify_queue);
+ assert(s->server);
+ assert(s->server->notify_fd >= 0);
+
+ /* Store the connection fd in PID 1, so that we get it passed
+ * in again on next start */
+
+ msghdr.msg_controllen = CMSG_SPACE(sizeof(int));
+ msghdr.msg_control = alloca0(msghdr.msg_controllen);
+
+ cmsg = CMSG_FIRSTHDR(&msghdr);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+
+ memcpy(CMSG_DATA(cmsg), &s->fd, sizeof(int));
+
+ l = sendmsg(s->server->notify_fd, &msghdr, MSG_DONTWAIT|MSG_NOSIGNAL);
+ if (l < 0) {
+ if (errno == EAGAIN)
+ return;
+
+ log_error_errno(errno, "Failed to send stream file descriptor to service manager: %m");
+ } else {
+ log_debug("Successfully sent stream file descriptor to service manager.");
+ s->fdstore = 1;
+ }
+
+ LIST_REMOVE(stdout_stream_notify_queue, s->server->stdout_streams_notify_queue, s);
+ s->in_notify_queue = false;
+
+}
diff --git a/src/journal/journald-stream.h b/src/journal/journald-stream.h
index 94bf955d78..e3497f0ded 100644
--- a/src/journal/journald-stream.h
+++ b/src/journal/journald-stream.h
@@ -21,9 +21,13 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+typedef struct StdoutStream StdoutStream;
+
#include "fdset.h"
#include "journald-server.h"
-int server_open_stdout_socket(Server *s, FDSet *fds);
+int server_open_stdout_socket(Server *s);
+int server_restore_streams(Server *s, FDSet *fds);
void stdout_stream_free(StdoutStream *s);
+void stdout_stream_send_notify(StdoutStream *s);
diff --git a/src/journal/journald-syslog.c b/src/journal/journald-syslog.c
index ffba451955..0be73088e2 100644
--- a/src/journal/journald-syslog.c
+++ b/src/journal/journald-syslog.c
@@ -19,20 +19,27 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
#include <stddef.h>
#include <sys/epoll.h>
+#include <unistd.h>
-#include "systemd/sd-messages.h"
-#include "socket-util.h"
-#include "selinux-util.h"
+#include "sd-messages.h"
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "formats-util.h"
+#include "io-util.h"
+#include "journald-console.h"
+#include "journald-kmsg.h"
#include "journald-server.h"
#include "journald-syslog.h"
-#include "journald-kmsg.h"
-#include "journald-console.h"
#include "journald-wall.h"
-#include "formats-util.h"
#include "process-util.h"
+#include "selinux-util.h"
+#include "socket-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
+#include "syslog-util.h"
/* Warn once every 30s if we missed syslog message */
#define WARN_FORWARD_SYSLOG_MISSED_USEC (30 * USEC_PER_SEC)
@@ -319,7 +326,7 @@ void server_process_syslog_message(
size_t label_len) {
char syslog_priority[sizeof("PRIORITY=") + DECIMAL_STR_MAX(int)],
- syslog_facility[sizeof("SYSLOG_FACILITY") + DECIMAL_STR_MAX(int)];
+ syslog_facility[sizeof("SYSLOG_FACILITY=") + DECIMAL_STR_MAX(int)];
const char *message = NULL, *syslog_identifier = NULL, *syslog_pid = NULL;
struct iovec iovec[N_IOVEC_META_FIELDS + 6];
unsigned n = 0;
@@ -350,11 +357,11 @@ void server_process_syslog_message(
IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=syslog");
- sprintf(syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK);
+ xsprintf(syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK);
IOVEC_SET_STRING(iovec[n++], syslog_priority);
if (priority & LOG_FACMASK) {
- sprintf(syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority));
+ xsprintf(syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority));
IOVEC_SET_STRING(iovec[n++], syslog_facility);
}
@@ -408,7 +415,7 @@ int server_open_syslog_socket(Server *s) {
return log_error_errno(errno, "SO_PASSCRED failed: %m");
#ifdef HAVE_SELINUX
- if (mac_selinux_use()) {
+ if (mac_selinux_have()) {
r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
if (r < 0)
log_warning_errno(errno, "SO_PASSSEC failed: %m");
@@ -423,6 +430,10 @@ int server_open_syslog_socket(Server *s) {
if (r < 0)
return log_error_errno(r, "Failed to add syslog server fd to event loop: %m");
+ r = sd_event_source_set_priority(s->syslog_event_source, SD_EVENT_PRIORITY_NORMAL+5);
+ if (r < 0)
+ return log_error_errno(r, "Failed to adjust syslog event source priority: %m");
+
return 0;
}
diff --git a/src/journal/journald-wall.c b/src/journal/journald-wall.c
index 7863766ae7..88bea3b86e 100644
--- a/src/journal/journald-wall.c
+++ b/src/journal/journald-wall.c
@@ -19,11 +19,13 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "utmp-wtmp.h"
+#include "alloc-util.h"
+#include "formats-util.h"
#include "journald-server.h"
#include "journald-wall.h"
-#include "formats-util.h"
#include "process-util.h"
+#include "string-util.h"
+#include "utmp-wtmp.h"
void server_forward_wall(
Server *s,
diff --git a/src/journal/journald.c b/src/journal/journald.c
index b2624c6d28..b9f5c099e1 100644
--- a/src/journal/journald.c
+++ b/src/journal/journald.c
@@ -21,16 +21,15 @@
#include <unistd.h>
-#include "systemd/sd-messages.h"
-#include "systemd/sd-daemon.h"
+#include "sd-daemon.h"
+#include "sd-messages.h"
+#include "formats-util.h"
#include "journal-authenticate.h"
-#include "journald-server.h"
#include "journald-kmsg.h"
+#include "journald-server.h"
#include "journald-syslog.h"
-
#include "sigbus.h"
-#include "formats-util.h"
int main(int argc, char *argv[]) {
Server server;
@@ -54,17 +53,13 @@ int main(int argc, char *argv[]) {
if (r < 0)
goto finish;
- server_vacuum(&server);
+ server_vacuum(&server, false, false);
server_flush_to_var(&server);
server_flush_dev_kmsg(&server);
log_debug("systemd-journald running as pid "PID_FMT, getpid());
server_driver_message(&server, SD_MESSAGE_JOURNAL_START, "Journal started");
- sd_notify(false,
- "READY=1\n"
- "STATUS=Processing requests...");
-
for (;;) {
usec_t t = USEC_INFINITY, n;
@@ -82,7 +77,7 @@ int main(int argc, char *argv[]) {
if (server.oldest_file_usec + server.max_retention_usec < n) {
log_info("Retention time reached.");
server_rotate(&server);
- server_vacuum(&server);
+ server_vacuum(&server, false, false);
continue;
}
@@ -117,10 +112,6 @@ int main(int argc, char *argv[]) {
server_driver_message(&server, SD_MESSAGE_JOURNAL_STOP, "Journal stopped");
finish:
- sd_notify(false,
- "STOPPING=1\n"
- "STATUS=Shutting down...");
-
server_done(&server);
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
diff --git a/src/journal/journald.conf b/src/journal/journald.conf
index 47eefe91c1..7beb96c671 100644
--- a/src/journal/journald.conf
+++ b/src/journal/journald.conf
@@ -22,9 +22,11 @@
#SystemMaxUse=
#SystemKeepFree=
#SystemMaxFileSize=
+#SystemMaxFiles=100
#RuntimeMaxUse=
#RuntimeKeepFree=
#RuntimeMaxFileSize=
+#RuntimeMaxFiles=100
#MaxRetentionSec=
#MaxFileSec=1month
#ForwardToSyslog=no
diff --git a/src/journal/lookup3.c b/src/journal/lookup3.c
index 52ffdf7b1d..3d791234f4 100644
--- a/src/journal/lookup3.c
+++ b/src/journal/lookup3.c
@@ -40,10 +40,10 @@ on 1 byte), but shoehorning those bytes into integers efficiently is messy.
*/
/* #define SELF_TEST 1 */
-#include <stdio.h> /* defines printf for tests */
-#include <time.h> /* defines time_t for timings in the test */
#include <stdint.h> /* defines uint32_t etc */
+#include <stdio.h> /* defines printf for tests */
#include <sys/param.h> /* attempt to define endianness */
+#include <time.h> /* defines time_t for timings in the test */
#ifdef linux
# include <endian.h> /* attempt to define endianness */
#endif
diff --git a/src/journal/mmap-cache.c b/src/journal/mmap-cache.c
index 22f75540b8..eb4b092e80 100644
--- a/src/journal/mmap-cache.c
+++ b/src/journal/mmap-cache.c
@@ -23,13 +23,15 @@
#include <stdlib.h>
#include <sys/mman.h>
+#include "alloc-util.h"
+#include "fd-util.h"
#include "hashmap.h"
#include "list.h"
#include "log.h"
-#include "util.h"
#include "macro.h"
-#include "sigbus.h"
#include "mmap-cache.h"
+#include "sigbus.h"
+#include "util.h"
typedef struct Window Window;
typedef struct Context Context;
@@ -288,7 +290,7 @@ static void fd_free(FileDescriptor *f) {
window_free(f->windows);
if (f->cache)
- assert_se(hashmap_remove(f->cache->fds, INT_TO_PTR(f->fd + 1)));
+ assert_se(hashmap_remove(f->cache->fds, FD_TO_PTR(f->fd)));
free(f);
}
@@ -300,7 +302,7 @@ static FileDescriptor* fd_add(MMapCache *m, int fd) {
assert(m);
assert(fd >= 0);
- f = hashmap_get(m->fds, INT_TO_PTR(fd + 1));
+ f = hashmap_get(m->fds, FD_TO_PTR(fd));
if (f)
return f;
@@ -315,7 +317,7 @@ static FileDescriptor* fd_add(MMapCache *m, int fd) {
f->cache = m;
f->fd = fd;
- r = hashmap_put(m->fds, UINT_TO_PTR(fd + 1), f);
+ r = hashmap_put(m->fds, FD_TO_PTR(fd), f);
if (r < 0) {
free(f);
return NULL;
@@ -346,7 +348,10 @@ static void mmap_cache_free(MMapCache *m) {
}
MMapCache* mmap_cache_unref(MMapCache *m) {
- assert(m);
+
+ if (!m)
+ return NULL;
+
assert(m->n_ref > 0);
m->n_ref --;
@@ -428,7 +433,7 @@ static int find_mmap(
assert(fd >= 0);
assert(size > 0);
- f = hashmap_get(m->fds, INT_TO_PTR(fd + 1));
+ f = hashmap_get(m->fds, FD_TO_PTR(fd));
if (!f)
return 0;
@@ -678,7 +683,7 @@ bool mmap_cache_got_sigbus(MMapCache *m, int fd) {
mmap_cache_process_sigbus(m);
- f = hashmap_get(m->fds, INT_TO_PTR(fd + 1));
+ f = hashmap_get(m->fds, FD_TO_PTR(fd));
if (!f)
return false;
@@ -697,7 +702,7 @@ void mmap_cache_close_fd(MMapCache *m, int fd) {
mmap_cache_process_sigbus(m);
- f = hashmap_get(m->fds, INT_TO_PTR(fd + 1));
+ f = hashmap_get(m->fds, FD_TO_PTR(fd));
if (!f)
return;
diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c
index 13fa9b52fc..cd5160154a 100644
--- a/src/journal/sd-journal.c
+++ b/src/journal/sd-journal.c
@@ -21,29 +21,38 @@
#include <errno.h>
#include <fcntl.h>
+#include <linux/magic.h>
+#include <poll.h>
#include <stddef.h>
-#include <unistd.h>
#include <sys/inotify.h>
-#include <poll.h>
#include <sys/vfs.h>
-#include <linux/magic.h>
+#include <unistd.h>
#include "sd-journal.h"
+
+#include "alloc-util.h"
+#include "catalog.h"
+#include "compress.h"
+#include "dirent-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "formats-util.h"
+#include "fs-util.h"
+#include "hashmap.h"
+#include "hostname-util.h"
+#include "io-util.h"
#include "journal-def.h"
#include "journal-file.h"
-#include "hashmap.h"
+#include "journal-internal.h"
#include "list.h"
-#include "strv.h"
-#include "path-util.h"
#include "lookup3.h"
-#include "compress.h"
-#include "journal-internal.h"
#include "missing.h"
-#include "catalog.h"
+#include "path-util.h"
#include "replace-var.h"
-#include "fileio.h"
-#include "formats-util.h"
-#include "hostname-util.h"
+#include "stat-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
+#include "strv.h"
#define JOURNAL_FILES_MAX 7168
@@ -64,19 +73,46 @@ static bool journal_pid_changed(sd_journal *j) {
return j->original_pid != getpid();
}
-/* We return an error here only if we didn't manage to
- memorize the real error. */
-static int set_put_error(sd_journal *j, int r) {
+static int journal_put_error(sd_journal *j, int r, const char *path) {
+ char *copy;
int k;
+ /* Memorize an error we encountered, and store which
+ * file/directory it was generated from. Note that we store
+ * only *one* path per error code, as the error code is the
+ * key into the hashmap, and the path is the value. This means
+ * we keep track only of all error kinds, but not of all error
+ * locations. This has the benefit that the hashmap cannot
+ * grow beyond bounds.
+ *
+ * We return an error here only if we didn't manage to
+ * memorize the real error. */
+
if (r >= 0)
return r;
- k = set_ensure_allocated(&j->errors, NULL);
+ k = hashmap_ensure_allocated(&j->errors, NULL);
if (k < 0)
return k;
- return set_put(j->errors, INT_TO_PTR(r));
+ if (path) {
+ copy = strdup(path);
+ if (!copy)
+ return -ENOMEM;
+ } else
+ copy = NULL;
+
+ k = hashmap_put(j->errors, INT_TO_PTR(r), copy);
+ if (k < 0) {
+ free(copy);
+
+ if (k == -EEXIST)
+ return 0;
+
+ return k;
+ }
+
+ return 0;
}
static void detach_location(sd_journal *j) {
@@ -1016,8 +1052,6 @@ _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
_public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) {
int r;
- const char *word, *state;
- size_t l;
Object *o;
assert_return(j, -EINVAL);
@@ -1031,20 +1065,23 @@ _public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) {
if (r < 0)
return r;
- FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
+ for(;;) {
_cleanup_free_ char *item = NULL;
- sd_id128_t id;
unsigned long long ll;
+ sd_id128_t id;
int k = 0;
- if (l < 2 || word[1] != '=')
- return -EINVAL;
+ r = extract_first_word(&cursor, &item, ";", EXTRACT_DONT_COALESCE_SEPARATORS);
+ if (r < 0)
+ return r;
- item = strndup(word, l);
- if (!item)
- return -ENOMEM;
+ if (r == 0)
+ break;
- switch (word[0]) {
+ if (strlen(item) < 2 || item[1] != '=')
+ return -EINVAL;
+
+ switch (item[0]) {
case 's':
k = sd_id128_from_string(item+2, &id);
@@ -1173,6 +1210,8 @@ static bool file_has_type_prefix(const char *prefix, const char *filename) {
}
static bool file_type_wanted(int flags, const char *filename) {
+ assert(filename);
+
if (!endswith(filename, ".journal") && !endswith(filename, ".journal~"))
return false;
@@ -1197,7 +1236,7 @@ static bool file_type_wanted(int flags, const char *filename) {
static int add_any_file(sd_journal *j, const char *path) {
JournalFile *f = NULL;
- int r;
+ int r, k;
assert(j);
assert(path);
@@ -1206,20 +1245,23 @@ static int add_any_file(sd_journal *j, const char *path) {
return 0;
if (ordered_hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
- log_warning("Too many open journal files, not adding %s.", path);
- return set_put_error(j, -ETOOMANYREFS);
+ log_debug("Too many open journal files, not adding %s.", path);
+ r = -ETOOMANYREFS;
+ goto fail;
}
r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f);
- if (r < 0)
- return r;
+ if (r < 0) {
+ log_debug_errno(r, "Failed to open journal file %s: %m", path);
+ goto fail;
+ }
/* journal_file_dump(f); */
r = ordered_hashmap_put(j->files, f->path, f);
if (r < 0) {
journal_file_close(f);
- return r;
+ goto fail;
}
log_debug("File %s added.", f->path);
@@ -1229,11 +1271,17 @@ static int add_any_file(sd_journal *j, const char *path) {
j->current_invalidate_counter ++;
return 0;
+
+fail:
+ k = journal_put_error(j, r, path);
+ if (k < 0)
+ return k;
+
+ return r;
}
static int add_file(sd_journal *j, const char *prefix, const char *filename) {
- _cleanup_free_ char *path = NULL;
- int r;
+ const char *path;
assert(j);
assert(prefix);
@@ -1243,34 +1291,24 @@ static int add_file(sd_journal *j, const char *prefix, const char *filename) {
!file_type_wanted(j->flags, filename))
return 0;
- path = strjoin(prefix, "/", filename, NULL);
- if (!path)
- return -ENOMEM;
-
- r = add_any_file(j, path);
- if (r == -ENOENT)
- return 0;
- return r;
+ path = strjoina(prefix, "/", filename);
+ return add_any_file(j, path);
}
-static int remove_file(sd_journal *j, const char *prefix, const char *filename) {
- _cleanup_free_ char *path;
+static void remove_file(sd_journal *j, const char *prefix, const char *filename) {
+ const char *path;
JournalFile *f;
assert(j);
assert(prefix);
assert(filename);
- path = strjoin(prefix, "/", filename, NULL);
- if (!path)
- return -ENOMEM;
-
+ path = strjoina(prefix, "/", filename);
f = ordered_hashmap_get(j->files, path);
if (!f)
- return 0;
+ return;
remove_file_real(j, f);
- return 0;
}
static void remove_file_real(sd_journal *j, JournalFile *f) {
@@ -1299,12 +1337,27 @@ static void remove_file_real(sd_journal *j, JournalFile *f) {
j->current_invalidate_counter ++;
}
+static int dirname_is_machine_id(const char *fn) {
+ sd_id128_t id, machine;
+ int r;
+
+ r = sd_id128_get_machine(&machine);
+ if (r < 0)
+ return r;
+
+ r = sd_id128_from_string(fn, &id);
+ if (r < 0)
+ return r;
+
+ return sd_id128_equal(id, machine);
+}
+
static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
_cleanup_free_ char *path = NULL;
- int r;
_cleanup_closedir_ DIR *d = NULL;
- sd_id128_t id, mid;
+ struct dirent *de = NULL;
Directory *m;
+ int r, k;
assert(j);
assert(prefix);
@@ -1313,35 +1366,36 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname)
log_debug("Considering %s/%s.", prefix, dirname);
if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
- (sd_id128_from_string(dirname, &id) < 0 ||
- sd_id128_get_machine(&mid) < 0 ||
- !(sd_id128_equal(id, mid) || path_startswith(prefix, "/run"))))
+ !(dirname_is_machine_id(dirname) > 0 || path_startswith(prefix, "/run")))
return 0;
path = strjoin(prefix, "/", dirname, NULL);
- if (!path)
- return -ENOMEM;
+ if (!path) {
+ r = -ENOMEM;
+ goto fail;
+ }
d = opendir(path);
if (!d) {
- log_debug_errno(errno, "Failed to open %s: %m", path);
- if (errno == ENOENT)
- return 0;
- return -errno;
+ r = log_debug_errno(errno, "Failed to open directory %s: %m", path);
+ goto fail;
}
m = hashmap_get(j->directories_by_path, path);
if (!m) {
m = new0(Directory, 1);
- if (!m)
- return -ENOMEM;
+ if (!m) {
+ r = -ENOMEM;
+ goto fail;
+ }
m->is_root = false;
m->path = path;
if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
free(m);
- return -ENOMEM;
+ r = -ENOMEM;
+ goto fail;
}
path = NULL; /* avoid freeing in cleanup */
@@ -1363,41 +1417,30 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname)
inotify_rm_watch(j->inotify_fd, m->wd);
}
- for (;;) {
- struct dirent *de;
-
- errno = 0;
- de = readdir(d);
- if (!de && errno != 0) {
- r = -errno;
- log_debug_errno(errno, "Failed to read directory %s: %m", m->path);
- return r;
- }
- if (!de)
- break;
+ FOREACH_DIRENT_ALL(de, d, return log_debug_errno(errno, "Failed to read directory %s: %m", m->path)) {
if (dirent_is_file_with_suffix(de, ".journal") ||
- dirent_is_file_with_suffix(de, ".journal~")) {
- r = add_file(j, m->path, de->d_name);
- if (r < 0) {
- log_debug_errno(r, "Failed to add file %s/%s: %m",
- m->path, de->d_name);
- r = set_put_error(j, r);
- if (r < 0)
- return r;
- }
- }
+ dirent_is_file_with_suffix(de, ".journal~"))
+ (void) add_file(j, m->path, de->d_name);
}
check_network(j, dirfd(d));
return 0;
+
+fail:
+ k = journal_put_error(j, r, path ?: dirname);
+ if (k < 0)
+ return k;
+
+ return r;
}
-static int add_root_directory(sd_journal *j, const char *p) {
+static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) {
_cleanup_closedir_ DIR *d = NULL;
+ struct dirent *de;
Directory *m;
- int r;
+ int r, k;
assert(j);
assert(p);
@@ -1410,26 +1453,35 @@ static int add_root_directory(sd_journal *j, const char *p) {
p = strjoina(j->prefix, p);
d = opendir(p);
- if (!d)
- return -errno;
+ if (!d) {
+ if (errno == ENOENT && missing_ok)
+ return 0;
+
+ r = log_debug_errno(errno, "Failed to open root directory %s: %m", p);
+ goto fail;
+ }
m = hashmap_get(j->directories_by_path, p);
if (!m) {
m = new0(Directory, 1);
- if (!m)
- return -ENOMEM;
+ if (!m) {
+ r = -ENOMEM;
+ goto fail;
+ }
m->is_root = true;
m->path = strdup(p);
if (!m->path) {
free(m);
- return -ENOMEM;
+ r = -ENOMEM;
+ goto fail;
}
if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
free(m->path);
free(m);
- return -ENOMEM;
+ r = -ENOMEM;
+ goto fail;
}
j->current_invalidate_counter ++;
@@ -1452,42 +1504,27 @@ static int add_root_directory(sd_journal *j, const char *p) {
if (j->no_new_files)
return 0;
- for (;;) {
- struct dirent *de;
+ FOREACH_DIRENT_ALL(de, d, return log_debug_errno(errno, "Failed to read directory %s: %m", m->path)) {
sd_id128_t id;
- errno = 0;
- de = readdir(d);
- if (!de && errno != 0) {
- r = -errno;
- log_debug_errno(errno, "Failed to read directory %s: %m", m->path);
- return r;
- }
- if (!de)
- break;
-
if (dirent_is_file_with_suffix(de, ".journal") ||
- dirent_is_file_with_suffix(de, ".journal~")) {
- r = add_file(j, m->path, de->d_name);
- if (r < 0) {
- log_debug_errno(r, "Failed to add file %s/%s: %m",
- m->path, de->d_name);
- r = set_put_error(j, r);
- if (r < 0)
- return r;
- }
- } else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) &&
- sd_id128_from_string(de->d_name, &id) >= 0) {
-
- r = add_directory(j, m->path, de->d_name);
- if (r < 0)
- log_debug_errno(r, "Failed to add directory %s/%s: %m", m->path, de->d_name);
- }
+ dirent_is_file_with_suffix(de, ".journal~"))
+ (void) add_file(j, m->path, de->d_name);
+ else if (IN_SET(de->d_type, DT_DIR, DT_LNK, DT_UNKNOWN) &&
+ sd_id128_from_string(de->d_name, &id) >= 0)
+ (void) add_directory(j, m->path, de->d_name);
}
check_network(j, dirfd(d));
return 0;
+
+fail:
+ k = journal_put_error(j, r, p);
+ if (k < 0)
+ return k;
+
+ return r;
}
static void remove_directory(sd_journal *j, Directory *d) {
@@ -1512,8 +1549,8 @@ static void remove_directory(sd_journal *j, Directory *d) {
}
static int add_search_paths(sd_journal *j) {
- int r;
- const char search_paths[] =
+
+ static const char search_paths[] =
"/run/log/journal\0"
"/var/log/journal\0";
const char *p;
@@ -1523,14 +1560,8 @@ static int add_search_paths(sd_journal *j) {
/* We ignore most errors here, since the idea is to only open
* what's actually accessible, and ignore the rest. */
- NULSTR_FOREACH(p, search_paths) {
- r = add_root_directory(j, p);
- if (r < 0 && r != -ENOENT) {
- r = set_put_error(j, r);
- if (r < 0)
- return r;
- }
- }
+ NULSTR_FOREACH(p, search_paths)
+ (void) add_root_directory(j, p, true);
return 0;
}
@@ -1554,17 +1585,14 @@ static int add_current_paths(sd_journal *j) {
if (!dir)
return -ENOMEM;
- r = add_root_directory(j, dir);
- if (r < 0) {
- set_put_error(j, r);
+ r = add_root_directory(j, dir, true);
+ if (r < 0)
return r;
- }
}
return 0;
}
-
static int allocate_inotify(sd_journal *j) {
assert(j);
@@ -1692,11 +1720,9 @@ _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int f
if (!j)
return -ENOMEM;
- r = add_root_directory(j, path);
- if (r < 0) {
- set_put_error(j, r);
+ r = add_root_directory(j, path, false);
+ if (r < 0)
goto fail;
- }
*ret = j;
return 0;
@@ -1721,10 +1747,8 @@ _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int fla
STRV_FOREACH(path, paths) {
r = add_any_file(j, *path);
- if (r < 0) {
- log_error_errno(r, "Failed to open %s: %m", *path);
+ if (r < 0)
goto fail;
- }
}
j->no_new_files = true;
@@ -1741,6 +1765,7 @@ fail:
_public_ void sd_journal_close(sd_journal *j) {
Directory *d;
JournalFile *f;
+ char *p;
if (!j)
return;
@@ -1768,10 +1793,13 @@ _public_ void sd_journal_close(sd_journal *j) {
mmap_cache_unref(j->mmap);
}
+ while ((p = hashmap_steal_first(j->errors)))
+ free(p);
+ hashmap_free(j->errors);
+
free(j->path);
free(j->prefix);
free(j->unique_field);
- set_free(j->errors);
free(j);
}
@@ -1912,10 +1940,14 @@ _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **
compression = o->object.flags & OBJECT_COMPRESSION_MASK;
if (compression) {
#if defined(HAVE_XZ) || defined(HAVE_LZ4)
- if (decompress_startswith(compression,
+ r = decompress_startswith(compression,
o->data.payload, l,
&f->compress_buffer, &f->compress_buffer_size,
- field, field_length, '=')) {
+ field, field_length, '=');
+ if (r < 0)
+ log_debug_errno(r, "Cannot decompress %s object of length %zu at offset "OFSfmt": %m",
+ object_compressed_to_string(compression), l, p);
+ else if (r > 0) {
size_t rsize;
@@ -2064,7 +2096,7 @@ _public_ int sd_journal_get_fd(sd_journal *j) {
if (j->no_new_files)
r = add_current_paths(j);
else if (j->path)
- r = add_root_directory(j, j->path);
+ r = add_root_directory(j, j->path, true);
else
r = add_search_paths(j);
if (r < 0)
@@ -2111,7 +2143,6 @@ _public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) {
static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
Directory *d;
- int r;
assert(j);
assert(e);
@@ -2127,20 +2158,10 @@ static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
/* Event for a journal file */
- if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
- r = add_file(j, d->path, e->name);
- if (r < 0) {
- log_debug_errno(r, "Failed to add file %s/%s: %m",
- d->path, e->name);
- set_put_error(j, r);
- }
-
- } else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) {
-
- r = remove_file(j, d->path, e->name);
- if (r < 0)
- log_debug_errno(r, "Failed to remove file %s/%s: %m", d->path, e->name);
- }
+ if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB))
+ (void) add_file(j, d->path, e->name);
+ else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT))
+ remove_file(j, d->path, e->name);
} else if (!d->is_root && e->len == 0) {
@@ -2153,11 +2174,8 @@ static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
/* Event for root directory */
- if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
- r = add_directory(j, d->path, e->name);
- if (r < 0)
- log_debug_errno(r, "Failed to add directory %s/%s: %m", d->path, e->name);
- }
+ if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB))
+ (void) add_directory(j, d->path, e->name);
}
return;
@@ -2166,7 +2184,7 @@ static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
if (e->mask & IN_IGNORED)
return;
- log_warning("Unknown inotify event.");
+ log_debug("Unknown inotify event.");
}
static int determine_change(sd_journal *j) {
diff --git a/src/journal/stacktrace.c b/src/journal/stacktrace.c
index 98a54ff269..4305462f80 100644
--- a/src/journal/stacktrace.c
+++ b/src/journal/stacktrace.c
@@ -22,10 +22,13 @@
#include <dwarf.h>
#include <elfutils/libdwfl.h>
-#include "util.h"
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "formats-util.h"
#include "macro.h"
#include "stacktrace.h"
-#include "formats-util.h"
+#include "string-util.h"
+#include "util.h"
#define FRAMES_MAX 64
#define THREADS_MAX 64
diff --git a/src/journal/test-catalog.c b/src/journal/test-catalog.c
index dbfdea609d..25980b7744 100644
--- a/src/journal/test-catalog.c
+++ b/src/journal/test-catalog.c
@@ -20,16 +20,21 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <locale.h>
-#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
+#include <locale.h>
+#include <unistd.h>
-#include "util.h"
-#include "log.h"
-#include "macro.h"
#include "sd-messages.h"
+
+#include "alloc-util.h"
#include "catalog.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "log.h"
+#include "macro.h"
+#include "string-util.h"
+#include "util.h"
static const char *catalog_dirs[] = {
CATALOG_DIR,
diff --git a/src/journal/test-compress-benchmark.c b/src/journal/test-compress-benchmark.c
index c8e5b76c6c..baed0d82a4 100644
--- a/src/journal/test-compress-benchmark.c
+++ b/src/journal/test-compress-benchmark.c
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/***
This file is part of systemd
@@ -17,30 +19,74 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "alloc-util.h"
#include "compress.h"
-#include "util.h"
#include "macro.h"
+#include "parse-util.h"
+#include "random-util.h"
+#include "string-util.h"
+#include "util.h"
-typedef int (compress_t)(const void *src, uint64_t src_size, void *dst, size_t *dst_size);
+typedef int (compress_t)(const void *src, uint64_t src_size, void *dst,
+ size_t dst_alloc_size, size_t *dst_size);
typedef int (decompress_t)(const void *src, uint64_t src_size,
void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max);
+static usec_t arg_duration = 2 * USEC_PER_SEC;
+static size_t arg_start;
+
#define MAX_SIZE (1024*1024LU)
+#define PRIME 1048571 /* A prime close enough to one megabyte that mod 4 == 3 */
+
+static size_t _permute(size_t x) {
+ size_t residue;
+
+ if (x >= PRIME)
+ return x;
+
+ residue = x*x % PRIME;
+ if (x <= PRIME / 2)
+ return residue;
+ else
+ return PRIME - residue;
+}
+
+static size_t permute(size_t x) {
+ return _permute((_permute(x) + arg_start) % MAX_SIZE ^ 0xFF345);
+}
-static char* make_buf(size_t count) {
+static char* make_buf(size_t count, const char *type) {
char *buf;
size_t i;
buf = malloc(count);
assert_se(buf);
- for (i = 0; i < count; i++)
- buf[i] = 'a' + i % ('z' - 'a' + 1);
+ if (streq(type, "zeros"))
+ memzero(buf, count);
+ else if (streq(type, "simple"))
+ for (i = 0; i < count; i++)
+ buf[i] = 'a' + i % ('z' - 'a' + 1);
+ else if (streq(type, "random")) {
+ size_t step = count / 10;
+
+ random_bytes(buf, step);
+ memzero(buf + 1*step, step);
+ random_bytes(buf + 2*step, step);
+ memzero(buf + 3*step, step);
+ random_bytes(buf + 4*step, step);
+ memzero(buf + 5*step, step);
+ random_bytes(buf + 6*step, step);
+ memzero(buf + 7*step, step);
+ random_bytes(buf + 8*step, step);
+ memzero(buf + 9*step, step);
+ } else
+ assert_not_reached("here");
return buf;
}
-static void test_compress_decompress(const char* label,
+static void test_compress_decompress(const char* label, const char* type,
compress_t compress, decompress_t decompress) {
usec_t n, n2 = 0;
float dt;
@@ -50,64 +96,85 @@ static void test_compress_decompress(const char* label,
size_t buf2_allocated = 0;
size_t skipped = 0, compressed = 0, total = 0;
- text = make_buf(MAX_SIZE);
+ text = make_buf(MAX_SIZE, type);
buf = calloc(MAX_SIZE + 1, 1);
assert_se(text && buf);
n = now(CLOCK_MONOTONIC);
- for (size_t i = 1; i <= MAX_SIZE; i += (i < 2048 ? 1 : 217)) {
- size_t j = 0, k = 0;
+ for (size_t i = 0; i <= MAX_SIZE; i++) {
+ size_t j = 0, k = 0, size;
int r;
- r = compress(text, i, buf, &j);
- /* assume compression must be successful except for small inputs */
- assert_se(r == 0 || (i < 2048 && r == -ENOBUFS));
+ size = permute(i);
+
+ log_debug("%s %zu %zu", type, i, size);
+
+ memzero(buf, MIN(size + 1000, MAX_SIZE));
+
+ r = compress(text, size, buf, size, &j);
+ /* assume compression must be successful except for small or random inputs */
+ assert_se(r == 0 || (size < 2048 && r == -ENOBUFS) || streq(type, "random"));
+
/* check for overwrites */
- assert_se(buf[i] == 0);
+ assert_se(buf[size] == 0);
if (r != 0) {
- skipped += i;
+ skipped += size;
continue;
}
assert_se(j > 0);
- if (j >= i)
- log_error("%s \"compressed\" %zu -> %zu", label, i, j);
+ if (j >= size)
+ log_error("%s \"compressed\" %zu -> %zu", label, size, j);
r = decompress(buf, j, &buf2, &buf2_allocated, &k, 0);
assert_se(r == 0);
assert_se(buf2_allocated >= k);
- assert_se(k == i);
+ assert_se(k == size);
- assert_se(memcmp(text, buf2, i) == 0);
+ assert_se(memcmp(text, buf2, size) == 0);
- total += i;
+ total += size;
compressed += j;
n2 = now(CLOCK_MONOTONIC);
- if (n2 - n > 60 * USEC_PER_SEC)
+ if (n2 - n > arg_duration)
break;
}
dt = (n2-n) / 1e6;
- log_info("%s: compressed & decompressed %zu bytes in %.2fs (%.2fMiB/s), "
+ log_info("%s/%s: compressed & decompressed %zu bytes in %.2fs (%.2fMiB/s), "
"mean compresion %.2f%%, skipped %zu bytes",
- label, total, dt,
+ label, type, total, dt,
total / 1024. / 1024 / dt,
100 - compressed * 100. / total,
skipped);
}
int main(int argc, char *argv[]) {
+ const char *i;
- log_set_max_level(LOG_DEBUG);
+ log_set_max_level(LOG_INFO);
+ if (argc >= 2) {
+ unsigned x;
+
+ assert_se(safe_atou(argv[1], &x) >= 0);
+ arg_duration = x * USEC_PER_SEC;
+ }
+ if (argc == 3)
+ (void) safe_atolu(argv[2], &arg_start);
+ else
+ arg_start = getpid();
+
+ NULSTR_FOREACH(i, "zeros\0simple\0random\0") {
#ifdef HAVE_XZ
- test_compress_decompress("XZ", compress_blob_xz, decompress_blob_xz);
+ test_compress_decompress("XZ", i, compress_blob_xz, decompress_blob_xz);
#endif
#ifdef HAVE_LZ4
- test_compress_decompress("LZ4", compress_blob_lz4, decompress_blob_lz4);
+ test_compress_decompress("LZ4", i, compress_blob_lz4, decompress_blob_lz4);
#endif
+ }
return 0;
}
diff --git a/src/journal/test-compress.c b/src/journal/test-compress.c
index f17c00e60d..68c9a4d76c 100644
--- a/src/journal/test-compress.c
+++ b/src/journal/test-compress.c
@@ -17,10 +17,17 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#ifdef HAVE_LZ4
+#include <lz4.h>
+#endif
+
+#include "alloc-util.h"
#include "compress.h"
-#include "util.h"
+#include "fd-util.h"
+#include "fileio.h"
#include "macro.h"
#include "random-util.h"
+#include "util.h"
#ifdef HAVE_XZ
# define XZ_OK 0
@@ -35,7 +42,7 @@
#endif
typedef int (compress_blob_t)(const void *src, uint64_t src_size,
- void *dst, size_t *dst_size);
+ void *dst, size_t dst_alloc_size, size_t *dst_size);
typedef int (decompress_blob_t)(const void *src, uint64_t src_size,
void **dst, size_t *dst_alloc_size,
size_t* dst_size, size_t dst_max);
@@ -54,15 +61,14 @@ static void test_compress_decompress(int compression,
size_t data_len,
bool may_fail) {
char compressed[512];
- size_t csize = 512;
- size_t usize = 0;
+ size_t csize, usize = 0;
_cleanup_free_ char *decompressed = NULL;
int r;
log_info("/* testing %s %s blob compression/decompression */",
object_compressed_to_string(compression), data);
- r = compress(data, data_len, compressed, &csize);
+ r = compress(data, data_len, compressed, sizeof(compressed), &csize);
if (r == -ENOBUFS) {
log_info_errno(r, "compression failed: %m");
assert_se(may_fail);
@@ -98,43 +104,45 @@ static void test_decompress_startswith(int compression,
size_t data_len,
bool may_fail) {
- char compressed[512];
- size_t csize = 512;
- size_t usize = 0;
- _cleanup_free_ char *decompressed = NULL;
+ char *compressed;
+ _cleanup_free_ char *compressed1 = NULL, *compressed2 = NULL, *decompressed = NULL;
+ size_t csize, usize = 0, len;
int r;
- log_info("/* testing decompress_startswith with %s on %s text*/",
+ log_info("/* testing decompress_startswith with %s on %.20s text*/",
object_compressed_to_string(compression), data);
- r = compress(data, data_len, compressed, &csize);
+#define BUFSIZE_1 512
+#define BUFSIZE_2 20000
+
+ compressed = compressed1 = malloc(BUFSIZE_1);
+ assert_se(compressed1);
+ r = compress(data, data_len, compressed, BUFSIZE_1, &csize);
if (r == -ENOBUFS) {
log_info_errno(r, "compression failed: %m");
assert_se(may_fail);
- return;
+
+ compressed = compressed2 = malloc(BUFSIZE_2);
+ assert_se(compressed2);
+ r = compress(data, data_len, compressed, BUFSIZE_2, &csize);
+ assert(r == 0);
}
assert_se(r == 0);
- assert_se(decompress_sw(compressed,
- csize,
- (void **) &decompressed,
- &usize,
- data, strlen(data), '\0') > 0);
- assert_se(decompress_sw(compressed,
- csize,
- (void **) &decompressed,
- &usize,
- data, strlen(data), 'w') == 0);
- assert_se(decompress_sw(compressed,
- csize,
- (void **) &decompressed,
- &usize,
- "barbarbar", 9, ' ') == 0);
- assert_se(decompress_sw(compressed,
- csize,
- (void **) &decompressed,
- &usize,
- data, strlen(data), '\0') > 0);
+ len = strlen(data);
+
+ r = decompress_sw(compressed, csize, (void **) &decompressed, &usize, data, len, '\0');
+ assert_se(r > 0);
+ r = decompress_sw(compressed, csize, (void **) &decompressed, &usize, data, len, 'w');
+ assert_se(r == 0);
+ r = decompress_sw(compressed, csize, (void **) &decompressed, &usize, "barbarbar", 9, ' ');
+ assert_se(r == 0);
+ r = decompress_sw(compressed, csize, (void **) &decompressed, &usize, data, len - 1, data[len-1]);
+ assert_se(r > 0);
+ r = decompress_sw(compressed, csize, (void **) &decompressed, &usize, data, len - 1, 'w');
+ assert_se(r == 0);
+ r = decompress_sw(compressed, csize, (void **) &decompressed, &usize, data, len, '\0');
+ assert_se(r > 0);
}
static void test_compress_stream(int compression,
@@ -144,8 +152,8 @@ static void test_compress_stream(int compression,
const char *srcfile) {
_cleanup_close_ int src = -1, dst = -1, dst2 = -1;
- char pattern[] = "/tmp/systemd-test.xz.XXXXXX",
- pattern2[] = "/tmp/systemd-test.xz.XXXXXX";
+ char pattern[] = "/tmp/systemd-test.compressed.XXXXXX",
+ pattern2[] = "/tmp/systemd-test.compressed.XXXXXX";
int r;
_cleanup_free_ char *cmd = NULL, *cmd2;
struct stat st = {};
@@ -185,7 +193,7 @@ static void test_compress_stream(int compression,
assert_se(lseek(dst, 1, SEEK_SET) == 1);
r = decompress(dst, dst2, st.st_size);
- assert_se(r == -EBADMSG);
+ assert_se(r == -EBADMSG || r == 0);
assert_se(lseek(dst, 0, SEEK_SET) == 0);
assert_se(lseek(dst2, 0, SEEK_SET) == 0);
@@ -196,6 +204,44 @@ static void test_compress_stream(int compression,
assert_se(unlink(pattern2) == 0);
}
+#ifdef HAVE_LZ4
+static void test_lz4_decompress_partial(void) {
+ char buf[20000];
+ size_t buf_size = sizeof(buf), compressed;
+ int r;
+ _cleanup_free_ char *huge = NULL;
+
+#define HUGE_SIZE (4096*1024)
+ huge = malloc(HUGE_SIZE);
+ memset(huge, 'x', HUGE_SIZE);
+ memcpy(huge, "HUGE=", 5);
+
+ r = LZ4_compress_limitedOutput(huge, buf, HUGE_SIZE, buf_size);
+ assert_se(r >= 0);
+ compressed = r;
+ log_info("Compressed %i → %zu", HUGE_SIZE, compressed);
+
+ r = LZ4_decompress_safe(buf, huge, r, HUGE_SIZE);
+ assert_se(r >= 0);
+ log_info("Decompressed → %i", r);
+
+ r = LZ4_decompress_safe_partial(buf, huge,
+ compressed,
+ 12, HUGE_SIZE);
+ assert_se(r >= 0);
+ log_info("Decompressed partial %i/%i → %i", 12, HUGE_SIZE, r);
+
+ /* We expect this to fail, because that's how current lz4 works. If this
+ * call succeeds, then lz4 has been fixed, and we need to change our code.
+ */
+ r = LZ4_decompress_safe_partial(buf, huge,
+ compressed,
+ 12, HUGE_SIZE-1);
+ assert_se(r < 0);
+ log_info("Decompressed partial %i/%i → %i", 12, HUGE_SIZE-1, r);
+}
+#endif
+
int main(int argc, char *argv[]) {
const char text[] =
"text\0foofoofoofoo AAAA aaaaaaaaa ghost busters barbarbar FFF"
@@ -203,6 +249,11 @@ int main(int argc, char *argv[]) {
char data[512] = "random\0";
+ char huge[4096*1024];
+ memset(huge, 'x', sizeof(huge));
+ memcpy(huge, "HUGE=", 5);
+ char_array_0(huge);
+
log_set_max_level(LOG_DEBUG);
random_bytes(data + 7, sizeof(data) - 7);
@@ -212,12 +263,17 @@ int main(int argc, char *argv[]) {
text, sizeof(text), false);
test_compress_decompress(OBJECT_COMPRESSED_XZ, compress_blob_xz, decompress_blob_xz,
data, sizeof(data), true);
+
test_decompress_startswith(OBJECT_COMPRESSED_XZ,
compress_blob_xz, decompress_startswith_xz,
text, sizeof(text), false);
test_decompress_startswith(OBJECT_COMPRESSED_XZ,
compress_blob_xz, decompress_startswith_xz,
data, sizeof(data), true);
+ test_decompress_startswith(OBJECT_COMPRESSED_XZ,
+ compress_blob_xz, decompress_startswith_xz,
+ huge, sizeof(huge), true);
+
test_compress_stream(OBJECT_COMPRESSED_XZ, "xzcat",
compress_stream_xz, decompress_stream_xz, argv[0]);
#else
@@ -229,16 +285,21 @@ int main(int argc, char *argv[]) {
text, sizeof(text), false);
test_compress_decompress(OBJECT_COMPRESSED_LZ4, compress_blob_lz4, decompress_blob_lz4,
data, sizeof(data), true);
+
test_decompress_startswith(OBJECT_COMPRESSED_LZ4,
compress_blob_lz4, decompress_startswith_lz4,
text, sizeof(text), false);
test_decompress_startswith(OBJECT_COMPRESSED_LZ4,
compress_blob_lz4, decompress_startswith_lz4,
data, sizeof(data), true);
+ test_decompress_startswith(OBJECT_COMPRESSED_LZ4,
+ compress_blob_lz4, decompress_startswith_lz4,
+ huge, sizeof(huge), true);
- /* Produced stream is not compatible with lz4 binary, skip lz4cat check. */
- test_compress_stream(OBJECT_COMPRESSED_LZ4, NULL,
+ test_compress_stream(OBJECT_COMPRESSED_LZ4, "lz4cat",
compress_stream_lz4, decompress_stream_lz4, argv[0]);
+
+ test_lz4_decompress_partial();
#else
log_info("/* LZ4 test skipped */");
#endif
diff --git a/src/journal/test-journal-enum.c b/src/journal/test-journal-enum.c
index cde2025ae9..d396fabdab 100644
--- a/src/journal/test-journal-enum.c
+++ b/src/journal/test-journal-enum.c
@@ -21,14 +21,15 @@
#include <stdio.h>
-#include "log.h"
#include "sd-journal.h"
-#include "macro.h"
+
#include "journal-internal.h"
+#include "log.h"
+#include "macro.h"
int main(int argc, char *argv[]) {
unsigned n = 0;
- _cleanup_journal_close_ sd_journal*j = NULL;
+ _cleanup_(sd_journal_closep) sd_journal*j = NULL;
log_set_max_level(LOG_DEBUG);
diff --git a/src/journal/test-journal-flush.c b/src/journal/test-journal-flush.c
index 2d4f531e9b..03d1522e23 100644
--- a/src/journal/test-journal-flush.c
+++ b/src/journal/test-journal-flush.c
@@ -22,9 +22,12 @@
#include <fcntl.h>
#include "sd-journal.h"
-#include "macro.h"
+
+#include "alloc-util.h"
#include "journal-file.h"
#include "journal-internal.h"
+#include "macro.h"
+#include "string-util.h"
int main(int argc, char *argv[]) {
_cleanup_free_ char *fn = NULL;
diff --git a/src/journal/test-journal-init.c b/src/journal/test-journal-init.c
index e6599f366d..142da85041 100644
--- a/src/journal/test-journal-init.c
+++ b/src/journal/test-journal-init.c
@@ -19,11 +19,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "systemd/sd-journal.h"
+#include "sd-journal.h"
#include "log.h"
-#include "util.h"
+#include "parse-util.h"
#include "rm-rf.h"
+#include "util.h"
int main(int argc, char *argv[]) {
sd_journal *j;
diff --git a/src/journal/test-journal-interleaving.c b/src/journal/test-journal-interleaving.c
index adefa1b026..5c055ef748 100644
--- a/src/journal/test-journal-interleaving.c
+++ b/src/journal/test-journal-interleaving.c
@@ -20,15 +20,18 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
#include <fcntl.h>
+#include <unistd.h>
#include "sd-journal.h"
+
+#include "alloc-util.h"
#include "journal-file.h"
#include "journal-vacuum.h"
-#include "util.h"
#include "log.h"
+#include "parse-util.h"
#include "rm-rf.h"
+#include "util.h"
/* This program tests skipping around in a multi-file journal.
*/
@@ -197,7 +200,7 @@ static void test_skip(void (*setup)(void)) {
if (arg_keep)
log_info("Not removing %s", t);
else {
- journal_directory_vacuum(".", 3000000, 0, NULL, true);
+ journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}
@@ -282,7 +285,7 @@ static void test_sequence_numbers(void) {
if (arg_keep)
log_info("Not removing %s", t);
else {
- journal_directory_vacuum(".", 3000000, 0, NULL, true);
+ journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}
diff --git a/src/journal/test-journal-match.c b/src/journal/test-journal-match.c
index a3187053c9..4ebaa8b31a 100644
--- a/src/journal/test-journal-match.c
+++ b/src/journal/test-journal-match.c
@@ -21,14 +21,16 @@
#include <stdio.h>
-#include "systemd/sd-journal.h"
+#include "sd-journal.h"
+#include "alloc-util.h"
#include "journal-internal.h"
-#include "util.h"
#include "log.h"
+#include "string-util.h"
+#include "util.h"
int main(int argc, char *argv[]) {
- _cleanup_journal_close_ sd_journal*j;
+ _cleanup_(sd_journal_closep) sd_journal*j = NULL;
_cleanup_free_ char *t;
log_set_max_level(LOG_DEBUG);
diff --git a/src/journal/test-journal-send.c b/src/journal/test-journal-send.c
index 81ca47ed8d..e537c1fe5f 100644
--- a/src/journal/test-journal-send.c
+++ b/src/journal/test-journal-send.c
@@ -19,58 +19,84 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "systemd/sd-journal.h"
+#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
-#include "log.h"
+#include "sd-journal.h"
+
+#include "macro.h"
int main(int argc, char *argv[]) {
char huge[4096*1024];
- log_set_max_level(LOG_DEBUG);
-
- sd_journal_print(LOG_INFO, "piepapo");
-
- sd_journal_send("MESSAGE=foobar",
- "VALUE=%i", 7,
- NULL);
+ /* utf-8 and non-utf-8, message-less and message-ful iovecs */
+ struct iovec graph1[] = {
+ {(char*) "GRAPH=graph", strlen("GRAPH=graph")}
+ };
+ struct iovec graph2[] = {
+ {(char*) "GRAPH=graph\n", strlen("GRAPH=graph\n")}
+ };
+ struct iovec message1[] = {
+ {(char*) "MESSAGE=graph", strlen("MESSAGE=graph")}
+ };
+ struct iovec message2[] = {
+ {(char*) "MESSAGE=graph\n", strlen("MESSAGE=graph\n")}
+ };
+
+ assert_se(sd_journal_print(LOG_INFO, "piepapo") == 0);
+
+ assert_se(sd_journal_send("MESSAGE=foobar",
+ "VALUE=%i", 7,
+ NULL) == 0);
errno = ENOENT;
- sd_journal_perror("Foobar");
+ assert_se(sd_journal_perror("Foobar") == 0);
- sd_journal_perror("");
+ assert_se(sd_journal_perror("") == 0);
memset(huge, 'x', sizeof(huge));
memcpy(huge, "HUGE=", 5);
char_array_0(huge);
- sd_journal_send("MESSAGE=Huge field attached",
- huge,
- NULL);
+ assert_se(sd_journal_send("MESSAGE=Huge field attached",
+ huge,
+ NULL) == 0);
- sd_journal_send("MESSAGE=uiui",
- "VALUE=A",
- "VALUE=B",
- "VALUE=C",
- "SINGLETON=1",
- "OTHERVALUE=X",
- "OTHERVALUE=Y",
- "WITH_BINARY=this is a binary value \a",
- NULL);
+ assert_se(sd_journal_send("MESSAGE=uiui",
+ "VALUE=A",
+ "VALUE=B",
+ "VALUE=C",
+ "SINGLETON=1",
+ "OTHERVALUE=X",
+ "OTHERVALUE=Y",
+ "WITH_BINARY=this is a binary value \a",
+ NULL) == 0);
syslog(LOG_NOTICE, "Hello World!");
- sd_journal_print(LOG_NOTICE, "Hello World");
-
- sd_journal_send("MESSAGE=Hello World!",
- "MESSAGE_ID=52fb62f99e2c49d89cfbf9d6de5e3555",
- "PRIORITY=5",
- "HOME=%s", getenv("HOME"),
- "TERM=%s", getenv("TERM"),
- "PAGE_SIZE=%li", sysconf(_SC_PAGESIZE),
- "N_CPUS=%li", sysconf(_SC_NPROCESSORS_ONLN),
- NULL);
+ assert_se(sd_journal_print(LOG_NOTICE, "Hello World") == 0);
+
+ assert_se(sd_journal_send("MESSAGE=Hello World!",
+ "MESSAGE_ID=52fb62f99e2c49d89cfbf9d6de5e3555",
+ "PRIORITY=5",
+ "HOME=%s", getenv("HOME"),
+ "TERM=%s", getenv("TERM"),
+ "PAGE_SIZE=%li", sysconf(_SC_PAGESIZE),
+ "N_CPUS=%li", sysconf(_SC_NPROCESSORS_ONLN),
+ NULL) == 0);
+
+ assert_se(sd_journal_sendv(graph1, 1) == 0);
+ assert_se(sd_journal_sendv(graph2, 1) == 0);
+ assert_se(sd_journal_sendv(message1, 1) == 0);
+ assert_se(sd_journal_sendv(message2, 1) == 0);
+
+ /* test without location fields */
+#undef sd_journal_sendv
+ assert_se(sd_journal_sendv(graph1, 1) == 0);
+ assert_se(sd_journal_sendv(graph2, 1) == 0);
+ assert_se(sd_journal_sendv(message1, 1) == 0);
+ assert_se(sd_journal_sendv(message2, 1) == 0);
sleep(1);
diff --git a/src/journal/test-journal-stream.c b/src/journal/test-journal-stream.c
index b5ecf2f375..2c257e43b6 100644
--- a/src/journal/test-journal-stream.c
+++ b/src/journal/test-journal-stream.c
@@ -19,16 +19,19 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
#include <fcntl.h>
+#include <unistd.h>
#include "sd-journal.h"
-#include "util.h"
+
+#include "alloc-util.h"
+#include "journal-file.h"
+#include "journal-internal.h"
#include "log.h"
#include "macro.h"
+#include "parse-util.h"
#include "rm-rf.h"
-#include "journal-file.h"
-#include "journal-internal.h"
+#include "util.h"
#define N_ENTRIES 200
@@ -76,7 +79,7 @@ int main(int argc, char *argv[]) {
JournalFile *one, *two, *three;
char t[] = "/tmp/journal-stream-XXXXXX";
unsigned i;
- _cleanup_journal_close_ sd_journal *j = NULL;
+ _cleanup_(sd_journal_closep) sd_journal *j = NULL;
char *z;
const void *data;
size_t l;
diff --git a/src/journal/test-journal-syslog.c b/src/journal/test-journal-syslog.c
index c99ca0654b..1784187fe9 100644
--- a/src/journal/test-journal-syslog.c
+++ b/src/journal/test-journal-syslog.c
@@ -19,8 +19,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "alloc-util.h"
#include "journald-syslog.h"
#include "macro.h"
+#include "string-util.h"
static void test_syslog_parse_identifier(const char* str,
const char *ident, const char*pid, int ret) {
diff --git a/src/journal/test-journal-verify.c b/src/journal/test-journal-verify.c
index d89123dc64..a7abb11fba 100644
--- a/src/journal/test-journal-verify.c
+++ b/src/journal/test-journal-verify.c
@@ -19,16 +19,17 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
-#include <fcntl.h>
-#include "util.h"
-#include "log.h"
-#include "rm-rf.h"
+#include "fd-util.h"
#include "journal-file.h"
#include "journal-verify.h"
+#include "log.h"
+#include "rm-rf.h"
#include "terminal-util.h"
+#include "util.h"
#define N_ENTRIES 6000
#define RANDOM_RANGE 77
diff --git a/src/journal/test-journal.c b/src/journal/test-journal.c
index caaab258c9..266e0d5473 100644
--- a/src/journal/test-journal.c
+++ b/src/journal/test-journal.c
@@ -22,11 +22,11 @@
#include <fcntl.h>
#include <unistd.h>
-#include "log.h"
-#include "rm-rf.h"
-#include "journal-file.h"
#include "journal-authenticate.h"
+#include "journal-file.h"
#include "journal-vacuum.h"
+#include "log.h"
+#include "rm-rf.h"
static bool arg_keep = false;
@@ -116,7 +116,7 @@ static void test_non_empty(void) {
if (arg_keep)
log_info("Not removing %s", t);
else {
- journal_directory_vacuum(".", 3000000, 0, NULL, true);
+ journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}
@@ -155,7 +155,7 @@ static void test_empty(void) {
if (arg_keep)
log_info("Not removing %s", t);
else {
- journal_directory_vacuum(".", 3000000, 0, NULL, true);
+ journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}
diff --git a/src/journal/test-mmap-cache.c b/src/journal/test-mmap-cache.c
index 3258b22702..fdd48e531c 100644
--- a/src/journal/test-mmap-cache.c
+++ b/src/journal/test-mmap-cache.c
@@ -19,14 +19,16 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <fcntl.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>
-#include <fcntl.h>
+#include "fd-util.h"
+#include "fileio.h"
#include "macro.h"
-#include "util.h"
#include "mmap-cache.h"
+#include "util.h"
int main(int argc, char *argv[]) {
int x, y, z, r;
diff --git a/src/libsystemd-network/arp-util.c b/src/libsystemd-network/arp-util.c
index 2f5b9b3731..4660c7ea09 100644
--- a/src/libsystemd-network/arp-util.c
+++ b/src/libsystemd-network/arp-util.c
@@ -21,8 +21,9 @@
#include <linux/filter.h>
#include <arpa/inet.h>
-#include "util.h"
#include "arp-util.h"
+#include "fd-util.h"
+#include "util.h"
int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_addr *eth_mac) {
struct sock_filter filter[] = {
diff --git a/src/libsystemd-network/arp-util.h b/src/libsystemd-network/arp-util.h
index 44e5c893a7..63c559f8dd 100644
--- a/src/libsystemd-network/arp-util.h
+++ b/src/libsystemd-network/arp-util.h
@@ -23,8 +23,8 @@
#include <netinet/if_ether.h>
-#include "sparse-endian.h"
#include "socket-util.h"
+#include "sparse-endian.h"
int arp_network_bind_raw_socket(int index, be32_t address, const struct ether_addr *eth_mac);
diff --git a/src/libsystemd-network/dhcp-identifier.c b/src/libsystemd-network/dhcp-identifier.c
index 7d9cad2a70..d7ae865557 100644
--- a/src/libsystemd-network/dhcp-identifier.c
+++ b/src/libsystemd-network/dhcp-identifier.c
@@ -19,24 +19,23 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-
-#include "sd-id128.h"
#include "libudev.h"
-#include "udev-util.h"
-
-#include "virt.h"
-#include "sparse-endian.h"
-#include "siphash24.h"
+#include "sd-id128.h"
-#include "dhcp6-protocol.h"
#include "dhcp-identifier.h"
+#include "dhcp6-protocol.h"
#include "network-internal.h"
+#include "siphash24.h"
+#include "sparse-endian.h"
+#include "udev-util.h"
+#include "virt.h"
#define SYSTEMD_PEN 43793
#define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) {
sd_id128_t machine_id;
+ uint64_t hash;
int r;
assert(duid);
@@ -52,13 +51,13 @@ int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) {
*len = sizeof(duid->type) + sizeof(duid->en);
/* a bit of snake-oil perhaps, but no need to expose the machine-id
- directly */
- siphash24(duid->en.id, &machine_id, sizeof(machine_id), HASH_KEY.bytes);
+ directly; duid->en.id might not be aligned, so we need to copy */
+ hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes));
+ memcpy(duid->en.id, &hash, sizeof(duid->en.id));
return 0;
}
-
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 */
@@ -87,10 +86,12 @@ int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_i
}
if (name)
- siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
+ id = siphash24(name, strlen(name), HASH_KEY.bytes);
else
/* fall back to MAC address if no predictable name available */
- siphash24((uint8_t*)&id, mac, mac_len, HASH_KEY.bytes);
+ id = siphash24(mac, mac_len, HASH_KEY.bytes);
+
+ id = htole64(id);
/* fold into 32 bits */
unaligned_write_be32(_id, (id & 0xffffffff) ^ (id >> 32));
diff --git a/src/libsystemd-network/dhcp-identifier.h b/src/libsystemd-network/dhcp-identifier.h
index 95117915f4..2291736f8b 100644
--- a/src/libsystemd-network/dhcp-identifier.h
+++ b/src/libsystemd-network/dhcp-identifier.h
@@ -21,11 +21,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "sd-id128.h"
#include "macro.h"
#include "sparse-endian.h"
#include "unaligned.h"
-#include "sd-id128.h"
/* RFC 3315 section 9.1:
* A DUID can be no more than 128 octets long (not including the type code).
diff --git a/src/libsystemd-network/dhcp-internal.h b/src/libsystemd-network/dhcp-internal.h
index df6f882af5..67714fd099 100644
--- a/src/libsystemd-network/dhcp-internal.h
+++ b/src/libsystemd-network/dhcp-internal.h
@@ -22,15 +22,15 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdint.h>
#include <linux/if_packet.h>
-#include <net/if_arp.h>
#include <net/ethernet.h>
-
-#include "socket-util.h"
+#include <net/if_arp.h>
+#include <stdint.h>
#include "sd-dhcp-client.h"
+
#include "dhcp-protocol.h"
+#include "socket-util.h"
int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link,
uint32_t xid, const uint8_t *mac_addr,
@@ -47,8 +47,7 @@ int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, uint8_
typedef int (*dhcp_option_cb_t)(uint8_t code, uint8_t len,
const void *option, void *userdata);
-int dhcp_option_parse(DHCPMessage *message, size_t len,
- dhcp_option_cb_t cb, void *userdata);
+int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_cb_t cb, void *userdata, char **error_message);
int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
uint8_t type, uint16_t arp_type, size_t optlen,
@@ -62,13 +61,10 @@ void dhcp_packet_append_ip_headers(DHCPPacket *packet, be32_t source_addr,
int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum);
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_client*, sd_dhcp_client_unref);
-#define _cleanup_dhcp_client_unref_ _cleanup_(sd_dhcp_client_unrefp)
-
/* If we are invoking callbacks of a dhcp-client, ensure unreffing the
* client from the callback doesn't destroy the object we are working
* on */
#define DHCP_CLIENT_DONT_DESTROY(client) \
- _cleanup_dhcp_client_unref_ _unused_ sd_dhcp_client *_dont_destroy_##client = sd_dhcp_client_ref(client)
+ _cleanup_(sd_dhcp_client_unrefp) _unused_ sd_dhcp_client *_dont_destroy_##client = sd_dhcp_client_ref(client)
#define log_dhcp_client(client, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "DHCP CLIENT (0x%x): " fmt, client->xid, ##__VA_ARGS__)
diff --git a/src/libsystemd-network/dhcp-lease-internal.h b/src/libsystemd-network/dhcp-lease-internal.h
index c6b97ca8f7..9a96be6236 100644
--- a/src/libsystemd-network/dhcp-lease-internal.h
+++ b/src/libsystemd-network/dhcp-lease-internal.h
@@ -25,12 +25,11 @@
#include <stdint.h>
#include <linux/if_packet.h>
-#include "util.h"
-#include "list.h"
+#include "sd-dhcp-client.h"
#include "dhcp-protocol.h"
-
-#include "sd-dhcp-client.h"
+#include "list.h"
+#include "util.h"
struct sd_dhcp_route {
struct in_addr dst_addr;
@@ -103,6 +102,3 @@ int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const void *client_id, size_t
int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file);
int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_lease*, sd_dhcp_lease_unref);
-#define _cleanup_dhcp_lease_unref_ _cleanup_(sd_dhcp_lease_unrefp)
diff --git a/src/libsystemd-network/dhcp-network.c b/src/libsystemd-network/dhcp-network.c
index 7f10838de1..fac25e0fa2 100644
--- a/src/libsystemd-network/dhcp-network.c
+++ b/src/libsystemd-network/dhcp-network.c
@@ -18,18 +18,18 @@
***/
#include <errno.h>
-#include <sys/socket.h>
-#include <string.h>
-#include <linux/if_packet.h>
-#include <linux/if_infiniband.h>
#include <net/ethernet.h>
#include <net/if_arp.h>
#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
#include <linux/filter.h>
-
-#include "socket-util.h"
+#include <linux/if_infiniband.h>
+#include <linux/if_packet.h>
#include "dhcp-internal.h"
+#include "fd-util.h"
+#include "socket-util.h"
static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
uint32_t xid, const uint8_t *mac_addr,
diff --git a/src/libsystemd-network/dhcp-option.c b/src/libsystemd-network/dhcp-option.c
index 36be7d54ed..9f0d96e57d 100644
--- a/src/libsystemd-network/dhcp-option.c
+++ b/src/libsystemd-network/dhcp-option.c
@@ -19,10 +19,13 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdint.h>
-#include <string.h>
#include <errno.h>
+#include <stdint.h>
#include <stdio.h>
+#include <string.h>
+
+#include "alloc-util.h"
+#include "utf8.h"
#include "dhcp-internal.h"
@@ -31,14 +34,14 @@ static int option_append(uint8_t options[], size_t size, size_t *offset,
assert(options);
assert(offset);
- if (code != DHCP_OPTION_END)
+ if (code != SD_DHCP_OPTION_END)
/* always make sure there is space for an END option */
size --;
switch (code) {
- case DHCP_OPTION_PAD:
- case DHCP_OPTION_END:
+ case SD_DHCP_OPTION_PAD:
+ case SD_DHCP_OPTION_END:
if (size < *offset + 1)
return -ENOBUFS;
@@ -88,7 +91,7 @@ int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset,
else if (r == -ENOBUFS && (file || sname)) {
/* did not fit, but we have more buffers to try
close the options array and move the offset to its end */
- r = option_append(message->options, size, offset, DHCP_OPTION_END, 0, NULL);
+ r = option_append(message->options, size, offset, SD_DHCP_OPTION_END, 0, NULL);
if (r < 0)
return r;
@@ -109,7 +112,7 @@ int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset,
} else if (r == -ENOBUFS && sname) {
/* did not fit, but we have more buffers to try
close the file array and move the offset to its end */
- r = option_append(message->options, size, offset, DHCP_OPTION_END, 0, NULL);
+ r = option_append(message->options, size, offset, SD_DHCP_OPTION_END, 0, NULL);
if (r < 0)
return r;
@@ -139,72 +142,84 @@ int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset,
}
static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overload,
- uint8_t *message_type, dhcp_option_cb_t cb,
+ uint8_t *message_type, char **error_message, dhcp_option_cb_t cb,
void *userdata) {
uint8_t code, len;
+ const uint8_t *option;
size_t offset = 0;
while (offset < buflen) {
- switch (options[offset]) {
- case DHCP_OPTION_PAD:
- offset++;
+ code = options[offset ++];
- break;
+ switch (code) {
+ case SD_DHCP_OPTION_PAD:
+ continue;
- case DHCP_OPTION_END:
+ case SD_DHCP_OPTION_END:
return 0;
+ }
- case DHCP_OPTION_MESSAGE_TYPE:
- if (buflen < offset + 3)
- return -ENOBUFS;
+ if (buflen < offset + 1)
+ return -ENOBUFS;
- len = options[++offset];
+ len = options[offset ++];
+
+ if (buflen < offset + len)
+ return -EINVAL;
+
+ option = &options[offset];
+
+ switch (code) {
+ case SD_DHCP_OPTION_MESSAGE_TYPE:
if (len != 1)
return -EINVAL;
if (message_type)
- *message_type = options[++offset];
- else
- offset++;
-
- offset++;
+ *message_type = *option;
break;
- case DHCP_OPTION_OVERLOAD:
- if (buflen < offset + 3)
- return -ENOBUFS;
-
- len = options[++offset];
- if (len != 1)
+ case SD_DHCP_OPTION_ERROR_MESSAGE:
+ if (len == 0)
return -EINVAL;
- if (overload)
- *overload = options[++offset];
- else
- offset++;
+ if (error_message) {
+ _cleanup_free_ char *string = NULL;
- offset++;
+ /* Accept a trailing NUL byte */
+ if (memchr(option, 0, len - 1))
+ return -EINVAL;
- break;
+ string = strndup((const char *) option, len);
+ if (!string)
+ return -ENOMEM;
- default:
- if (buflen < offset + 3)
- return -ENOBUFS;
+ if (!ascii_is_valid(string))
+ return -EINVAL;
- code = options[offset];
- len = options[++offset];
+ free(*error_message);
+ *error_message = string;
+ string = NULL;
+ }
- if (buflen < ++offset + len)
+ break;
+ case SD_DHCP_OPTION_OVERLOAD:
+ if (len != 1)
return -EINVAL;
- if (cb)
- cb(code, len, &options[offset], userdata);
+ if (overload)
+ *overload = *option;
- offset += len;
+ break;
+
+ default:
+ if (cb)
+ cb(code, len, option, userdata);
break;
}
+
+ offset += len;
}
if (offset < buflen)
@@ -213,8 +228,8 @@ static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overlo
return 0;
}
-int dhcp_option_parse(DHCPMessage *message, size_t len,
- dhcp_option_cb_t cb, void *userdata) {
+int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_cb_t cb, void *userdata, char **_error_message) {
+ _cleanup_free_ char *error_message = NULL;
uint8_t overload = 0;
uint8_t message_type = 0;
int r;
@@ -227,27 +242,29 @@ int dhcp_option_parse(DHCPMessage *message, size_t len,
len -= sizeof(DHCPMessage);
- r = parse_options(message->options, len, &overload, &message_type,
- cb, userdata);
+ r = parse_options(message->options, len, &overload, &message_type, &error_message, cb, userdata);
if (r < 0)
return r;
if (overload & DHCP_OVERLOAD_FILE) {
- r = parse_options(message->file, sizeof(message->file),
- NULL, &message_type, cb, userdata);
+ r = parse_options(message->file, sizeof(message->file), NULL, &message_type, &error_message, cb, userdata);
if (r < 0)
return r;
}
if (overload & DHCP_OVERLOAD_SNAME) {
- r = parse_options(message->sname, sizeof(message->sname),
- NULL, &message_type, cb, userdata);
+ r = parse_options(message->sname, sizeof(message->sname), NULL, &message_type, &error_message, cb, userdata);
if (r < 0)
return r;
}
- if (message_type)
- return message_type;
+ if (message_type == 0)
+ return -ENOMSG;
+
+ if (_error_message && IN_SET(message_type, DHCP_NAK, DHCP_DECLINE)) {
+ *_error_message = error_message;
+ error_message = NULL;
+ }
- return -ENOMSG;
+ return message_type;
}
diff --git a/src/libsystemd-network/dhcp-packet.c b/src/libsystemd-network/dhcp-packet.c
index cd7f5095ca..8d75d49691 100644
--- a/src/libsystemd-network/dhcp-packet.c
+++ b/src/libsystemd-network/dhcp-packet.c
@@ -19,13 +19,12 @@
***/
#include <errno.h>
-#include <string.h>
#include <net/ethernet.h>
#include <net/if_arp.h>
+#include <string.h>
-
-#include "dhcp-protocol.h"
#include "dhcp-internal.h"
+#include "dhcp-protocol.h"
#define DHCP_CLIENT_MIN_OPTIONS_SIZE 312
@@ -45,7 +44,7 @@ int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
message->magic = htobe32(DHCP_MAGIC_COOKIE);
r = dhcp_option_append(message, optlen, &offset, 0,
- DHCP_OPTION_MESSAGE_TYPE, 1, &type);
+ SD_DHCP_OPTION_MESSAGE_TYPE, 1, &type);
if (r < 0)
return r;
diff --git a/src/libsystemd-network/dhcp-protocol.h b/src/libsystemd-network/dhcp-protocol.h
index 88a81d2866..18490def06 100644
--- a/src/libsystemd-network/dhcp-protocol.h
+++ b/src/libsystemd-network/dhcp-protocol.h
@@ -21,8 +21,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <netinet/udp.h>
#include <netinet/ip.h>
+#include <netinet/udp.h>
#include <stdint.h>
#include "macro.h"
@@ -105,42 +105,11 @@ enum {
DHCP_OVERLOAD_SNAME = 2,
};
+#define DHCP_MAX_FQDN_LENGTH 255
+
enum {
- DHCP_OPTION_PAD = 0,
- DHCP_OPTION_SUBNET_MASK = 1,
- DHCP_OPTION_TIME_OFFSET = 2,
- DHCP_OPTION_ROUTER = 3,
- DHCP_OPTION_DOMAIN_NAME_SERVER = 6,
- DHCP_OPTION_HOST_NAME = 12,
- DHCP_OPTION_BOOT_FILE_SIZE = 13,
- DHCP_OPTION_DOMAIN_NAME = 15,
- DHCP_OPTION_ROOT_PATH = 17,
- DHCP_OPTION_ENABLE_IP_FORWARDING = 19,
- DHCP_OPTION_ENABLE_IP_FORWARDING_NL = 20,
- DHCP_OPTION_POLICY_FILTER = 21,
- DHCP_OPTION_INTERFACE_MDR = 22,
- DHCP_OPTION_INTERFACE_TTL = 23,
- DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT = 24,
- DHCP_OPTION_INTERFACE_MTU = 26,
- DHCP_OPTION_BROADCAST = 28,
- DHCP_OPTION_STATIC_ROUTE = 33,
- DHCP_OPTION_NTP_SERVER = 42,
- DHCP_OPTION_VENDOR_SPECIFIC = 43,
- DHCP_OPTION_REQUESTED_IP_ADDRESS = 50,
- DHCP_OPTION_IP_ADDRESS_LEASE_TIME = 51,
- DHCP_OPTION_OVERLOAD = 52,
- DHCP_OPTION_MESSAGE_TYPE = 53,
- DHCP_OPTION_SERVER_IDENTIFIER = 54,
- DHCP_OPTION_PARAMETER_REQUEST_LIST = 55,
- DHCP_OPTION_MAXIMUM_MESSAGE_SIZE = 57,
- DHCP_OPTION_RENEWAL_T1_TIME = 58,
- DHCP_OPTION_REBINDING_T2_TIME = 59,
- DHCP_OPTION_VENDOR_CLASS_IDENTIFIER = 60,
- DHCP_OPTION_CLIENT_IDENTIFIER = 61,
- DHCP_OPTION_NEW_POSIX_TIMEZONE = 100,
- DHCP_OPTION_NEW_TZDB_TIMEZONE = 101,
- DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121,
- DHCP_OPTION_PRIVATE_BASE = 224,
- DHCP_OPTION_PRIVATE_LAST = 254,
- DHCP_OPTION_END = 255,
+ DHCP_FQDN_FLAG_S = (1 << 0),
+ DHCP_FQDN_FLAG_O = (1 << 1),
+ DHCP_FQDN_FLAG_E = (1 << 2),
+ DHCP_FQDN_FLAG_N = (1 << 3),
};
diff --git a/src/libsystemd-network/dhcp-server-internal.h b/src/libsystemd-network/dhcp-server-internal.h
index 5dc3c7aa26..2e30e93df9 100644
--- a/src/libsystemd-network/dhcp-server-internal.h
+++ b/src/libsystemd-network/dhcp-server-internal.h
@@ -22,14 +22,13 @@
#pragma once
-#include "sd-event.h"
#include "sd-dhcp-server.h"
+#include "sd-event.h"
+#include "dhcp-internal.h"
#include "hashmap.h"
-#include "util.h"
#include "log.h"
-
-#include "dhcp-internal.h"
+#include "util.h"
typedef struct DHCPClientId {
size_t length;
@@ -85,9 +84,6 @@ typedef struct DHCPRequest {
uint32_t lifetime;
} DHCPRequest;
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_server*, sd_dhcp_server_unref);
-#define _cleanup_dhcp_server_unref_ _cleanup_(sd_dhcp_server_unrefp)
-
#define log_dhcp_server(client, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "DHCP SERVER: " fmt, ##__VA_ARGS__)
int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
@@ -96,5 +92,5 @@ int dhcp_server_send_packet(sd_dhcp_server *server,
DHCPRequest *req, DHCPPacket *packet,
int type, size_t optoffset);
-unsigned long client_id_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]);
+void client_id_hash_func(const void *p, struct siphash *state);
int client_id_compare_func(const void *_a, const void *_b);
diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h
index 83e8192f58..ecc220f2f6 100644
--- a/src/libsystemd-network/dhcp6-internal.h
+++ b/src/libsystemd-network/dhcp6-internal.h
@@ -24,10 +24,11 @@
#include <net/ethernet.h>
#include <netinet/in.h>
-#include "sparse-endian.h"
#include "sd-event.h"
+
#include "list.h"
#include "macro.h"
+#include "sparse-endian.h"
typedef struct DHCP6Address DHCP6Address;
@@ -58,9 +59,6 @@ typedef struct DHCP6IA DHCP6IA;
#define log_dhcp6_client(p, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "DHCPv6 CLIENT: " fmt, ##__VA_ARGS__)
-int dhcp_network_icmp6_bind_router_solicitation(int index);
-int dhcp_network_icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr);
-
int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
size_t optlen, const void *optval);
int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia);
diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h
index 4edecf7711..3bfb46b96d 100644
--- a/src/libsystemd-network/dhcp6-lease-internal.h
+++ b/src/libsystemd-network/dhcp6-lease-internal.h
@@ -25,6 +25,7 @@
#include <stdint.h>
#include "sd-dhcp6-lease.h"
+
#include "dhcp6-internal.h"
struct sd_dhcp6_lease {
@@ -73,6 +74,3 @@ int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval,
size_t optlen) ;
int dhcp6_lease_new(sd_dhcp6_lease **ret);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_lease*, sd_dhcp6_lease_unref);
-#define _cleanup_dhcp6_lease_free_ _cleanup_(sd_dhcp6_lease_unrefp)
diff --git a/src/libsystemd-network/dhcp6-network.c b/src/libsystemd-network/dhcp6-network.c
index 187975364b..fd2d60c9d5 100644
--- a/src/libsystemd-network/dhcp6-network.c
+++ b/src/libsystemd-network/dhcp6-network.c
@@ -18,148 +18,47 @@
***/
#include <errno.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <string.h>
-#include <linux/if_packet.h>
+#include <netinet/in.h>
+#include <netinet/ip6.h>
#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
#include <unistd.h>
-#include <netinet/ip6.h>
-#include <netinet/icmp6.h>
-#include <netinet/in.h>
-
-#include "socket-util.h"
+#include <linux/if_packet.h>
#include "dhcp6-internal.h"
#include "dhcp6-protocol.h"
-
-#define IN6ADDR_ALL_ROUTERS_MULTICAST_INIT \
- { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } } }
-
-#define IN6ADDR_ALL_NODES_MULTICAST_INIT \
- { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } }
-
-int dhcp_network_icmp6_bind_router_solicitation(int index) {
- struct icmp6_filter filter = { };
- struct ipv6_mreq mreq = {
- .ipv6mr_multiaddr = IN6ADDR_ALL_NODES_MULTICAST_INIT,
- .ipv6mr_interface = index,
- };
- _cleanup_close_ int s = -1;
- int r, zero = 0, hops = 255;
-
- s = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK,
- IPPROTO_ICMPV6);
- if (s < 0)
- return -errno;
-
- ICMP6_FILTER_SETBLOCKALL(&filter);
- ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
- r = setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filter,
- sizeof(filter));
- if (r < 0)
- return -errno;
-
- /* RFC 3315, section 6.7, bullet point 2 may indicate that an
- IPV6_PKTINFO socket option also applies for ICMPv6 multicast.
- Empirical experiments indicates otherwise and therefore an
- IPV6_MULTICAST_IF socket option is used here instead */
- r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &index,
- sizeof(index));
- if (r < 0)
- return -errno;
-
- r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &zero,
- sizeof(zero));
- if (r < 0)
- return -errno;
-
- r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops,
- sizeof(hops));
- if (r < 0)
- return -errno;
-
- r = setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq,
- sizeof(mreq));
- if (r < 0)
- return -errno;
-
- r = s;
- s = -1;
- return r;
-}
-
-int dhcp_network_icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) {
- struct sockaddr_in6 dst = {
- .sin6_family = AF_INET6,
- .sin6_addr = IN6ADDR_ALL_ROUTERS_MULTICAST_INIT,
- };
- struct {
- struct nd_router_solicit rs;
- struct nd_opt_hdr rs_opt;
- struct ether_addr rs_opt_mac;
- } _packed_ rs = {
- .rs.nd_rs_type = ND_ROUTER_SOLICIT,
- };
- struct iovec iov[1] = {
- { &rs, },
- };
- struct msghdr msg = {
- .msg_name = &dst,
- .msg_namelen = sizeof(dst),
- .msg_iov = iov,
- .msg_iovlen = 1,
- };
- int r;
-
- if (ether_addr) {
- memcpy(&rs.rs_opt_mac, ether_addr, ETH_ALEN);
- rs.rs_opt.nd_opt_type = ND_OPT_SOURCE_LINKADDR;
- rs.rs_opt.nd_opt_len = 1;
- iov[0].iov_len = sizeof(rs);
- } else
- iov[0].iov_len = sizeof(rs.rs);
-
- r = sendmsg(s, &msg, 0);
- if (r < 0)
- return -errno;
-
- return 0;
-}
+#include "fd-util.h"
+#include "socket-util.h"
int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
- struct in6_pktinfo pktinfo = {
- .ipi6_ifindex = index,
- };
union sockaddr_union src = {
.in6.sin6_family = AF_INET6,
.in6.sin6_port = htobe16(DHCP6_PORT_CLIENT),
- .in6.sin6_addr = IN6ADDR_ANY_INIT,
+ .in6.sin6_scope_id = index,
};
_cleanup_close_ int s = -1;
int r, off = 0, on = 1;
- if (local_address)
- memcpy(&src.in6.sin6_addr, local_address,
- sizeof(src.in6.sin6_addr));
+ assert(index > 0);
+ assert(local_address);
- s = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
- IPPROTO_UDP);
+ src.in6.sin6_addr = *local_address;
+
+ s = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_UDP);
if (s < 0)
return -errno;
- r = setsockopt(s, IPPROTO_IPV6, IPV6_PKTINFO, &pktinfo,
- sizeof(pktinfo));
+ r = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
if (r < 0)
return -errno;
- r = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
+ r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, sizeof(off));
if (r < 0)
return -errno;
- r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, sizeof(off));
+ r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
if (r < 0)
return -errno;
diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c
index f41bebced0..6050851858 100644
--- a/src/libsystemd-network/dhcp6-option.c
+++ b/src/libsystemd-network/dhcp6-option.c
@@ -19,18 +19,20 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <netinet/in.h>
#include <errno.h>
+#include <netinet/in.h>
#include <string.h>
-#include "sparse-endian.h"
-#include "unaligned.h"
-#include "util.h"
-#include "strv.h"
+#include "sd-dhcp6-client.h"
+#include "alloc-util.h"
#include "dhcp6-internal.h"
#include "dhcp6-protocol.h"
#include "dns-domain.h"
+#include "sparse-endian.h"
+#include "strv.h"
+#include "unaligned.h"
+#include "util.h"
#define DHCP6_OPTION_IA_NA_LEN 12
#define DHCP6_OPTION_IA_TA_LEN 4
@@ -90,11 +92,11 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) {
assert_return(buf && *buf && buflen && ia, -EINVAL);
switch (ia->type) {
- case DHCP6_OPTION_IA_NA:
+ case SD_DHCP6_OPTION_IA_NA:
len = DHCP6_OPTION_IA_NA_LEN;
break;
- case DHCP6_OPTION_IA_TA:
+ case SD_DHCP6_OPTION_IA_TA:
len = DHCP6_OPTION_IA_TA_LEN;
break;
@@ -117,7 +119,7 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) {
*buflen -= len;
LIST_FOREACH(addresses, addr, ia->addresses) {
- r = option_append_hdr(buf, buflen, DHCP6_OPTION_IAADDR,
+ r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IAADDR,
sizeof(addr->iaaddr));
if (r < 0)
return r;
@@ -196,7 +198,7 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
assert_return(!ia->addresses, -EINVAL);
switch (iatype) {
- case DHCP6_OPTION_IA_NA:
+ case SD_DHCP6_OPTION_IA_NA:
if (*buflen < DHCP6_OPTION_IA_NA_LEN + sizeof(DHCP6Option) +
sizeof(addr->iaaddr)) {
@@ -219,7 +221,7 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
break;
- case DHCP6_OPTION_IA_TA:
+ case SD_DHCP6_OPTION_IA_TA:
if (*buflen < DHCP6_OPTION_IA_TA_LEN + sizeof(DHCP6Option) +
sizeof(addr->iaaddr)) {
r = -ENOBUFS;
@@ -247,7 +249,7 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
while ((r = option_parse_hdr(buf, buflen, &opt, &optlen)) >= 0) {
switch (opt) {
- case DHCP6_OPTION_IAADDR:
+ case SD_DHCP6_OPTION_IAADDR:
addr = new0(DHCP6Address, 1);
if (!addr) {
@@ -274,7 +276,7 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
break;
- case DHCP6_OPTION_STATUS_CODE:
+ case SD_DHCP6_OPTION_STATUS_CODE:
if (optlen < sizeof(status))
break;
@@ -344,7 +346,7 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char *
int r;
assert_return(optlen > 1, -ENODATA);
- assert_return(optval[optlen] == '\0', -EINVAL);
+ assert_return(optval[optlen - 1] == '\0', -EINVAL);
while (pos < optlen) {
_cleanup_free_ char *ret = NULL;
@@ -360,7 +362,6 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char *
/* End of name */
break;
else if (c <= 63) {
- _cleanup_free_ char *t = NULL;
const char *label;
/* Literal label */
@@ -369,21 +370,20 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char *
if (pos > optlen)
return -EMSGSIZE;
- r = dns_label_escape(label, c, &t);
- if (r < 0)
- goto fail;
-
- if (!GREEDY_REALLOC0(ret, allocated, n + !first + strlen(t) + 1)) {
+ if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) {
r = -ENOMEM;
goto fail;
}
- if (!first)
- ret[n++] = '.';
- else
+ if (first)
first = false;
+ else
+ ret[n++] = '.';
+
+ r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX);
+ if (r < 0)
+ goto fail;
- memcpy(ret + n, t, r);
n += r;
continue;
} else {
diff --git a/src/libsystemd-network/dhcp6-protocol.h b/src/libsystemd-network/dhcp6-protocol.h
index b3a28f88b4..246cc94cd8 100644
--- a/src/libsystemd-network/dhcp6-protocol.h
+++ b/src/libsystemd-network/dhcp6-protocol.h
@@ -99,41 +99,6 @@ enum {
};
enum {
- DHCP6_OPTION_CLIENTID = 1,
- DHCP6_OPTION_SERVERID = 2,
- DHCP6_OPTION_IA_NA = 3,
- DHCP6_OPTION_IA_TA = 4,
- DHCP6_OPTION_IAADDR = 5,
- DHCP6_OPTION_ORO = 6,
- DHCP6_OPTION_PREFERENCE = 7,
- DHCP6_OPTION_ELAPSED_TIME = 8,
- DHCP6_OPTION_RELAY_MSG = 9,
- /* option code 10 is unassigned */
- DHCP6_OPTION_AUTH = 11,
- DHCP6_OPTION_UNICAST = 12,
- DHCP6_OPTION_STATUS_CODE = 13,
- DHCP6_OPTION_RAPID_COMMIT = 14,
- DHCP6_OPTION_USER_CLASS = 15,
- DHCP6_OPTION_VENDOR_CLASS = 16,
- DHCP6_OPTION_VENDOR_OPTS = 17,
- DHCP6_OPTION_INTERFACE_ID = 18,
- DHCP6_OPTION_RECONF_MSG = 19,
- DHCP6_OPTION_RECONF_ACCEPT = 20,
-
- DHCP6_OPTION_DNS_SERVERS = 23, /* RFC 3646 */
- DHCP6_OPTION_DOMAIN_LIST = 24, /* RFC 3646 */
-
- DHCP6_OPTION_SNTP_SERVERS = 31, /* RFC 4075, deprecated */
-
- /* option code 35 is unassigned */
-
- DHCP6_OPTION_NTP_SERVER = 56, /* RFC 5908 */
-
- /* option codes 89-142 are unassigned */
- /* option codes 144-65535 are unassigned */
-};
-
-enum {
DHCP6_NTP_SUBOPTION_SRV_ADDR = 1,
DHCP6_NTP_SUBOPTION_MC_ADDR = 2,
DHCP6_NTP_SUBOPTION_SRV_FQDN = 3,
diff --git a/src/libsystemd-network/icmp6-util.c b/src/libsystemd-network/icmp6-util.c
new file mode 100644
index 0000000000..acad9d7d6a
--- /dev/null
+++ b/src/libsystemd-network/icmp6-util.c
@@ -0,0 +1,127 @@
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Intel Corporation. All rights reserved.
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General 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 <netinet/icmp6.h>
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <linux/if_packet.h>
+
+#include "fd-util.h"
+#include "icmp6-util.h"
+#include "socket-util.h"
+
+#define IN6ADDR_ALL_ROUTERS_MULTICAST_INIT \
+ { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } } }
+
+#define IN6ADDR_ALL_NODES_MULTICAST_INIT \
+ { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } }
+
+int icmp6_bind_router_solicitation(int index) {
+ struct icmp6_filter filter = { };
+ struct ipv6_mreq mreq = {
+ .ipv6mr_multiaddr = IN6ADDR_ALL_NODES_MULTICAST_INIT,
+ .ipv6mr_interface = index,
+ };
+ _cleanup_close_ int s = -1;
+ int r, zero = 0, one = 1, hops = 255;
+
+ s = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_ICMPV6);
+ if (s < 0)
+ return -errno;
+
+ ICMP6_FILTER_SETBLOCKALL(&filter);
+ ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
+ r = setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter));
+ if (r < 0)
+ return -errno;
+
+ /* RFC 3315, section 6.7, bullet point 2 may indicate that an
+ IPV6_PKTINFO socket option also applies for ICMPv6 multicast.
+ Empirical experiments indicates otherwise and therefore an
+ IPV6_MULTICAST_IF socket option is used here instead */
+ r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &index, sizeof(index));
+ if (r < 0)
+ return -errno;
+
+ r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &zero, sizeof(zero));
+ if (r < 0)
+ return -errno;
+
+ r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, sizeof(hops));
+ if (r < 0)
+ return -errno;
+
+ r = setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
+ if (r < 0)
+ return -errno;
+
+ r = setsockopt(s, SOL_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one));
+ if (r < 0)
+ return -errno;
+
+ r = s;
+ s = -1;
+ return r;
+}
+
+int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) {
+ struct sockaddr_in6 dst = {
+ .sin6_family = AF_INET6,
+ .sin6_addr = IN6ADDR_ALL_ROUTERS_MULTICAST_INIT,
+ };
+ struct {
+ struct nd_router_solicit rs;
+ struct nd_opt_hdr rs_opt;
+ struct ether_addr rs_opt_mac;
+ } _packed_ rs = {
+ .rs.nd_rs_type = ND_ROUTER_SOLICIT,
+ .rs_opt.nd_opt_type = ND_OPT_SOURCE_LINKADDR,
+ .rs_opt.nd_opt_len = 1,
+ };
+ struct iovec iov = {
+ .iov_base = &rs,
+ .iov_len = sizeof(rs),
+ };
+ struct msghdr msg = {
+ .msg_name = &dst,
+ .msg_namelen = sizeof(dst),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ int r;
+
+ assert(s >= 0);
+ assert(ether_addr);
+
+ rs.rs_opt_mac = *ether_addr;
+
+ r = sendmsg(s, &msg, 0);
+ if (r < 0)
+ return -errno;
+
+ return 0;
+}
diff --git a/src/libsystemd-network/icmp6-util.h b/src/libsystemd-network/icmp6-util.h
new file mode 100644
index 0000000000..4eb17e152e
--- /dev/null
+++ b/src/libsystemd-network/icmp6-util.h
@@ -0,0 +1,27 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014-2015 Intel Corporation. All rights reserved.
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General 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 <net/ethernet.h>
+
+int icmp6_bind_router_solicitation(int index);
+int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr);
diff --git a/src/libsystemd-network/lldp-internal.c b/src/libsystemd-network/lldp-internal.c
index 3c04898e92..10f12d11a2 100644
--- a/src/libsystemd-network/lldp-internal.c
+++ b/src/libsystemd-network/lldp-internal.c
@@ -20,6 +20,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "sd-lldp.h"
+
+#include "alloc-util.h"
#include "lldp-internal.h"
/* We store maximum 1K chassis entries */
@@ -28,207 +31,6 @@
/* Maximum Ports can be attached to any chassis */
#define LLDP_MIB_MAX_PORT_PER_CHASSIS 32
-int lldp_read_chassis_id(tlv_packet *tlv,
- uint8_t *type,
- uint16_t *length,
- uint8_t **data) {
- uint8_t subtype;
- int r;
-
- assert_return(tlv, -EINVAL);
-
- r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_CHASSIS_ID);
- if (r < 0)
- goto out2;
-
- r = tlv_packet_read_u8(tlv, &subtype);
- if (r < 0)
- goto out1;
-
- switch (subtype) {
- case LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
-
- r = tlv_packet_read_bytes(tlv, data, length);
- if (r < 0)
- goto out1;
-
- break;
- default:
- r = -EOPNOTSUPP;
- break;
- }
-
- *type = subtype;
-
- out1:
- (void) lldp_tlv_packet_exit_container(tlv);
-
- out2:
- return r;
-}
-
-int lldp_read_port_id(tlv_packet *tlv,
- uint8_t *type,
- uint16_t *length,
- uint8_t **data) {
- uint8_t subtype;
- char *s;
- int r;
-
- assert_return(tlv, -EINVAL);
-
- r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_PORT_ID);
- if (r < 0)
- goto out2;
-
- r = tlv_packet_read_u8(tlv, &subtype);
- if (r < 0)
- goto out1;
-
- switch (subtype) {
- case LLDP_PORT_SUBTYPE_PORT_COMPONENT:
- case LLDP_PORT_SUBTYPE_INTERFACE_ALIAS:
- case LLDP_PORT_SUBTYPE_INTERFACE_NAME:
- case LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED:
-
- r = tlv_packet_read_string(tlv, &s, length);
- if (r < 0)
- goto out1;
-
- *data = (uint8_t *) s;
-
- break;
- case LLDP_PORT_SUBTYPE_MAC_ADDRESS:
-
- r = tlv_packet_read_bytes(tlv, data, length);
- if (r < 0)
- goto out1;
-
- break;
- default:
- r = -EOPNOTSUPP;
- break;
- }
-
- *type = subtype;
-
- out1:
- (void) lldp_tlv_packet_exit_container(tlv);
-
- out2:
- return r;
-}
-
-int lldp_read_ttl(tlv_packet *tlv, uint16_t *ttl) {
- int r;
-
- assert_return(tlv, -EINVAL);
-
- r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_TTL);
- if (r < 0)
- goto out;
-
- r = tlv_packet_read_u16(tlv, ttl);
-
- (void) lldp_tlv_packet_exit_container(tlv);
-
- out:
- return r;
-}
-
-int lldp_read_system_name(tlv_packet *tlv,
- uint16_t *length,
- char **data) {
- char *s;
- int r;
-
- assert_return(tlv, -EINVAL);
-
- r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_SYSTEM_NAME);
- if (r < 0)
- return r;
-
- r = tlv_packet_read_string(tlv, &s, length);
- if (r < 0)
- goto out;
-
- *data = (char *) s;
-
- out:
- (void) lldp_tlv_packet_exit_container(tlv);
-
- return r;
-}
-
-int lldp_read_system_description(tlv_packet *tlv,
- uint16_t *length,
- char **data) {
- char *s;
- int r;
-
- assert_return(tlv, -EINVAL);
-
- r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_SYSTEM_DESCRIPTION);
- if (r < 0)
- return r;
-
- r = tlv_packet_read_string(tlv, &s, length);
- if (r < 0)
- goto out;
-
- *data = (char *) s;
-
- out:
- (void) lldp_tlv_packet_exit_container(tlv);
-
- return r;
-}
-
-int lldp_read_port_description(tlv_packet *tlv,
- uint16_t *length,
- char **data) {
- char *s;
- int r;
-
- assert_return(tlv, -EINVAL);
-
- r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_PORT_DESCRIPTION);
- if (r < 0)
- return r;
-
- r = tlv_packet_read_string(tlv, &s, length);
- if (r < 0)
- goto out;
-
- *data = (char *) s;
-
- out:
- (void) lldp_tlv_packet_exit_container(tlv);
-
- return r;
-}
-
-int lldp_read_system_capability(tlv_packet *tlv, uint16_t *data) {
- int r;
-
- assert_return(tlv, -EINVAL);
-
- r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_SYSTEM_CAPABILITIES);
- if (r < 0)
- return r;
-
- r = tlv_packet_read_u16(tlv, data);
- if (r < 0)
- goto out;
-
- return 0;
- out:
-
- (void) lldp_tlv_packet_exit_container(tlv);
-
- return r;
-}
-
/* 10.5.5.2.2 mibUpdateObjects ()
* The mibUpdateObjects () procedure updates the MIB objects corresponding to
* the TLVs contained in the received LLDPDU for the LLDP remote system
@@ -244,7 +46,7 @@ int lldp_mib_update_objects(lldp_chassis *c, tlv_packet *tlv) {
assert_return(c, -EINVAL);
assert_return(tlv, -EINVAL);
- r = lldp_read_port_id(tlv, &type, &length, &data);
+ r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length);
if (r < 0)
return r;
@@ -253,13 +55,13 @@ int lldp_mib_update_objects(lldp_chassis *c, tlv_packet *tlv) {
if ((p->type == type && p->length == length && !memcmp(p->data, data, p->length))) {
- r = lldp_read_ttl(tlv, &ttl);
+ r = sd_lldp_packet_read_ttl(tlv, &ttl);
if (r < 0)
return r;
p->until = ttl * USEC_PER_SEC + now(clock_boottime_or_monotonic());
- tlv_packet_free(p->packet);
+ sd_lldp_packet_unref(p->packet);
p->packet = tlv;
prioq_reshuffle(p->c->by_expiry, p, &p->prioq_idx);
@@ -281,7 +83,7 @@ int lldp_mib_remove_objects(lldp_chassis *c, tlv_packet *tlv) {
assert_return(c, -EINVAL);
assert_return(tlv, -EINVAL);
- r = lldp_read_port_id(tlv, &type, &length, &data);
+ r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length);
if (r < 0)
return r;
@@ -312,11 +114,11 @@ int lldp_mib_add_objects(Prioq *by_expiry,
assert_return(neighbour_mib, -EINVAL);
assert_return(tlv, -EINVAL);
- r = lldp_read_chassis_id(tlv, &subtype, &length, &data);
+ r = sd_lldp_packet_read_chassis_id(tlv, &subtype, &data, &length);
if (r < 0)
goto drop;
- r = lldp_read_ttl(tlv, &ttl);
+ r = sd_lldp_packet_read_ttl(tlv, &ttl);
if (r < 0)
goto drop;
@@ -401,7 +203,7 @@ int lldp_mib_add_objects(Prioq *by_expiry,
return 0;
drop:
- tlv_packet_free(tlv);
+ sd_lldp_packet_unref(tlv);
if (new_chassis)
hashmap_remove(neighbour_mib, &c->chassis_id);
@@ -435,7 +237,7 @@ void lldp_neighbour_port_free(lldp_neighbour_port *p) {
if(!p)
return;
- tlv_packet_free(p->packet);
+ sd_lldp_packet_unref(p->packet);
free(p->data);
free(p);
@@ -452,11 +254,11 @@ int lldp_neighbour_port_new(lldp_chassis *c,
assert(tlv);
- r = lldp_read_port_id(tlv, &type, &length, &data);
+ r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length);
if (r < 0)
return r;
- r = lldp_read_ttl(tlv, &ttl);
+ r = sd_lldp_packet_read_ttl(tlv, &ttl);
if (r < 0)
return r;
@@ -505,7 +307,7 @@ int lldp_chassis_new(tlv_packet *tlv,
assert(tlv);
- r = lldp_read_chassis_id(tlv, &type, &length, &data);
+ r = sd_lldp_packet_read_chassis_id(tlv, &type, &data, &length);
if (r < 0)
return r;
@@ -531,3 +333,30 @@ int lldp_chassis_new(tlv_packet *tlv,
return 0;
}
+
+int lldp_receive_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ _cleanup_(sd_lldp_packet_unrefp) tlv_packet *packet = NULL;
+ tlv_packet *p;
+ uint16_t length;
+ int r;
+
+ assert(fd);
+ assert(userdata);
+
+ r = tlv_packet_new(&packet);
+ if (r < 0)
+ return r;
+
+ length = read(fd, &packet->pdu, sizeof(packet->pdu));
+
+ /* Silently drop the packet */
+ if ((size_t) length > ETHER_MAX_LEN)
+ return 0;
+
+ packet->userdata = userdata;
+
+ p = packet;
+ packet = NULL;
+
+ return lldp_handle_packet(p, (uint16_t) length);
+}
diff --git a/src/libsystemd-network/lldp-internal.h b/src/libsystemd-network/lldp-internal.h
index f4eadbb87e..5d19fa0fea 100644
--- a/src/libsystemd-network/lldp-internal.h
+++ b/src/libsystemd-network/lldp-internal.h
@@ -22,9 +22,11 @@
#pragma once
-#include "log.h"
+#include "sd-event.h"
+
#include "list.h"
#include "lldp-tlv.h"
+#include "log.h"
#include "prioq.h"
typedef struct lldp_neighbour_port lldp_neighbour_port;
@@ -86,13 +88,6 @@ int lldp_mib_update_objects(lldp_chassis *c, tlv_packet *tlv);
int lldp_mib_add_objects(Prioq *by_expiry, Hashmap *neighbour_mib, tlv_packet *tlv);
int lldp_mib_remove_objects(lldp_chassis *c, tlv_packet *tlv);
-int lldp_read_chassis_id(tlv_packet *tlv, uint8_t *type, uint16_t *length, uint8_t **data);
-int lldp_read_port_id(tlv_packet *tlv, uint8_t *type, uint16_t *length, uint8_t **data);
-int lldp_read_ttl(tlv_packet *tlv, uint16_t *ttl);
-int lldp_read_system_name(tlv_packet *tlv, uint16_t *length, char **data);
-int lldp_read_system_description(tlv_packet *tlv, uint16_t *length, char **data);
-int lldp_read_system_capability(tlv_packet *tlv, uint16_t *data);
-int lldp_read_port_description(tlv_packet *tlv, uint16_t *length, char **data);
-
int lldp_handle_packet(tlv_packet *m, uint16_t length);
+int lldp_receive_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata);
#define log_lldp(fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "LLDP: " fmt, ##__VA_ARGS__)
diff --git a/src/libsystemd-network/lldp-network.c b/src/libsystemd-network/lldp-network.c
index 664d2f7867..f483cd9c8e 100644
--- a/src/libsystemd-network/lldp-network.c
+++ b/src/libsystemd-network/lldp-network.c
@@ -23,10 +23,11 @@
#include <linux/filter.h>
#include <linux/if_ether.h>
-#include "socket-util.h"
-#include "lldp-tlv.h"
-#include "lldp-network.h"
+#include "fd-util.h"
#include "lldp-internal.h"
+#include "lldp-network.h"
+#include "lldp-tlv.h"
+#include "socket-util.h"
int lldp_network_bind_raw_socket(int ifindex) {
typedef struct LLDPFrame {
@@ -82,30 +83,3 @@ int lldp_network_bind_raw_socket(int ifindex) {
return r;
}
-
-int lldp_receive_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- _cleanup_tlv_packet_free_ tlv_packet *packet = NULL;
- tlv_packet *p;
- uint16_t length;
- int r;
-
- assert(fd);
- assert(userdata);
-
- r = tlv_packet_new(&packet);
- if (r < 0)
- return r;
-
- length = read(fd, &packet->pdu, sizeof(packet->pdu));
-
- /* Silently drop the packet */
- if ((size_t) length > ETHER_MAX_LEN)
- return 0;
-
- packet->userdata = userdata;
-
- p = packet;
- packet = NULL;
-
- return lldp_handle_packet(p, (uint16_t) length);
-}
diff --git a/src/libsystemd-network/lldp-network.h b/src/libsystemd-network/lldp-network.h
index b7f8d3bf80..74ee13a414 100644
--- a/src/libsystemd-network/lldp-network.h
+++ b/src/libsystemd-network/lldp-network.h
@@ -25,4 +25,3 @@
#include "sd-event.h"
int lldp_network_bind_raw_socket(int ifindex);
-int lldp_receive_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata);
diff --git a/src/libsystemd-network/lldp-port.c b/src/libsystemd-network/lldp-port.c
index aa6a3b9224..1f1a49adbf 100644
--- a/src/libsystemd-network/lldp-port.c
+++ b/src/libsystemd-network/lldp-port.c
@@ -20,9 +20,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "alloc-util.h"
#include "async.h"
-#include "lldp-port.h"
+#include "lldp-internal.h"
#include "lldp-network.h"
+#include "lldp-port.h"
int lldp_port_start(lldp_port *p) {
int r;
@@ -38,19 +40,19 @@ int lldp_port_start(lldp_port *p) {
r = sd_event_add_io(p->event, &p->lldp_port_rx,
p->rawfd, EPOLLIN, lldp_receive_packet, p);
if (r < 0) {
- log_debug("Failed to allocate event source: %s", strerror(-r));
- return r;
+ log_debug_errno(r, "Failed to allocate event source: %m");
+ goto fail;
}
r = sd_event_source_set_priority(p->lldp_port_rx, p->event_priority);
if (r < 0) {
- log_debug("Failed to set event priority: %s", strerror(-r));
+ log_debug_errno(r, "Failed to set event priority: %m");
goto fail;
}
r = sd_event_source_set_description(p->lldp_port_rx, "lldp-port-rx");
if (r < 0) {
- log_debug("Failed to set event name: %s", strerror(-r));
+ log_debug_errno(r, "Failed to set event name: %m");
goto fail;
}
diff --git a/src/libsystemd-network/lldp-tlv.c b/src/libsystemd-network/lldp-tlv.c
index 0cea5b10a6..66343147a1 100644
--- a/src/libsystemd-network/lldp-tlv.c
+++ b/src/libsystemd-network/lldp-tlv.c
@@ -20,11 +20,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <net/ethernet.h>
#include <arpa/inet.h>
+#include <net/ethernet.h>
-#include "macro.h"
+#include "alloc-util.h"
#include "lldp-tlv.h"
+#include "macro.h"
int tlv_section_new(tlv_section **ret) {
tlv_section *s;
@@ -54,22 +55,41 @@ int tlv_packet_new(tlv_packet **ret) {
return -ENOMEM;
LIST_HEAD_INIT(m->sections);
+ m->n_ref = 1;
*ret = m;
return 0;
}
-void tlv_packet_free(tlv_packet *m) {
+tlv_packet *sd_lldp_packet_ref(tlv_packet *m) {
+
+ if (!m)
+ return NULL;
+
+ assert(m->n_ref > 0);
+ m->n_ref++;
+
+ return m;
+}
+
+tlv_packet *sd_lldp_packet_unref(tlv_packet *m) {
tlv_section *s, *n;
if (!m)
- return;
+ return NULL;
+
+ assert(m->n_ref > 0);
+ m->n_ref--;
+
+ if (m->n_ref > 0)
+ return m;
LIST_FOREACH_SAFE(section, s, n, m->sections)
tlv_section_free(s);
free(m);
+ return NULL;
}
int tlv_packet_append_bytes(tlv_packet *m, const void *data, size_t data_length) {
@@ -221,9 +241,9 @@ int tlv_packet_read_string(tlv_packet *m, char **data, uint16_t *data_length) {
return r;
*data = (char *) val;
- *data_length = m->container->length;
+ *data_length = m->container->data + m->container->length - m->container->read_pos;
- m->container->read_pos += m->container->length;
+ m->container->read_pos += *data_length;
return 0;
}
@@ -239,9 +259,9 @@ int tlv_packet_read_bytes(tlv_packet *m, uint8_t **data, uint16_t *data_length)
return r;
*data = (uint8_t *) val;
- *data_length = m->container->length;
+ *data_length = m->container->data + m->container->length - m->container->read_pos;
- m->container->read_pos += m->container->length;
+ m->container->read_pos += *data_length;
return 0;
}
@@ -258,7 +278,7 @@ int tlv_packet_parse_pdu(tlv_packet *m, uint16_t size) {
p = m->pdu;
- /* extract ethernet herader */
+ /* extract Ethernet header */
memcpy(&m->mac, p, ETH_ALEN);
p += sizeof(struct ether_header);
@@ -278,6 +298,17 @@ int tlv_packet_parse_pdu(tlv_packet *m, uint16_t size) {
}
p += 2;
+
+ if (section->type == LLDP_TYPE_PRIVATE &&
+ section->length >= LLDP_OUI_LEN + 1) {
+ section->oui = p;
+ p += LLDP_OUI_LEN;
+ section->subtype = *p++;
+
+ section->length -= LLDP_OUI_LEN + 1;
+ l += LLDP_OUI_LEN + 1;
+ }
+
section->data = p;
LIST_FIND_TAIL(section, m->sections, tail);
@@ -294,6 +325,7 @@ int lldp_tlv_packet_enter_container(tlv_packet *m, uint16_t type) {
tlv_section *s;
assert_return(m, -EINVAL);
+ assert_return(type != LLDP_TYPE_PRIVATE, -EINVAL);
LIST_FOREACH(section, s, m->sections)
if (s->type == type)
@@ -305,7 +337,35 @@ int lldp_tlv_packet_enter_container(tlv_packet *m, uint16_t type) {
m->container->read_pos = s->data;
if (!m->container->read_pos) {
- m->container = 0;
+ m->container = NULL;
+ return -1;
+ }
+
+ return 0;
+}
+
+int lldp_tlv_packet_enter_container_oui(tlv_packet *m, const uint8_t *oui, uint8_t subtype) {
+ tlv_section *s;
+
+ assert_return(m, -EINVAL);
+ assert_return(oui, -EINVAL);
+
+ LIST_FOREACH(section, s, m->sections) {
+ if (s->type == LLDP_TYPE_PRIVATE &&
+ s->oui &&
+ s->subtype == subtype &&
+ !memcmp(s->oui, oui, LLDP_OUI_LEN))
+ break;
+ }
+
+ if (!s)
+ return -1;
+
+ m->container = s;
+
+ m->container->read_pos = s->data;
+ if (!m->container->read_pos) {
+ m->container = NULL;
return -1;
}
@@ -319,3 +379,262 @@ int lldp_tlv_packet_exit_container(tlv_packet *m) {
return 0;
}
+
+static int lldp_tlv_packet_read_u16_tlv(tlv_packet *tlv, uint16_t type, uint16_t *value) {
+ int r, r2;
+
+ assert_return(tlv, -EINVAL);
+
+ r = lldp_tlv_packet_enter_container(tlv, type);
+ if (r < 0)
+ return r;
+
+ r = tlv_packet_read_u16(tlv, value);
+ r2 = lldp_tlv_packet_exit_container(tlv);
+
+ return r < 0 ? r : r2;
+}
+
+static int lldp_tlv_packet_read_string_tlv(tlv_packet *tlv, uint16_t type, char **data, uint16_t *length) {
+ char *s;
+ int r, r2;
+
+ assert_return(tlv, -EINVAL);
+
+ r = lldp_tlv_packet_enter_container(tlv, type);
+ if (r < 0)
+ return r;
+
+ r = tlv_packet_read_string(tlv, &s, length);
+ if (r < 0)
+ goto out;
+
+ *data = (char *) s;
+
+ out:
+ r2 = lldp_tlv_packet_exit_container(tlv);
+
+ return r < 0 ? r : r2;
+}
+
+int sd_lldp_packet_read_chassis_id(tlv_packet *tlv,
+ uint8_t *type,
+ uint8_t **data,
+ uint16_t *length) {
+ uint8_t subtype;
+ int r, r2;
+
+ assert_return(tlv, -EINVAL);
+
+ r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_CHASSIS_ID);
+ if (r < 0)
+ return r;
+
+ r = tlv_packet_read_u8(tlv, &subtype);
+ if (r < 0)
+ goto out;
+
+ switch (subtype) {
+ case LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
+
+ r = tlv_packet_read_bytes(tlv, data, length);
+ if (r < 0)
+ goto out;
+
+ break;
+ default:
+ r = -EOPNOTSUPP;
+ break;
+ }
+
+ *type = subtype;
+
+ out:
+ r2 = lldp_tlv_packet_exit_container(tlv);
+
+ return r < 0 ? r : r2;
+}
+
+int sd_lldp_packet_read_port_id(tlv_packet *tlv,
+ uint8_t *type,
+ uint8_t **data,
+ uint16_t *length) {
+ uint8_t subtype;
+ char *s;
+ int r, r2;
+
+ assert_return(tlv, -EINVAL);
+
+ r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_PORT_ID);
+ if (r < 0)
+ return r;
+
+ r = tlv_packet_read_u8(tlv, &subtype);
+ if (r < 0)
+ goto out;
+
+ switch (subtype) {
+ case LLDP_PORT_SUBTYPE_PORT_COMPONENT:
+ case LLDP_PORT_SUBTYPE_INTERFACE_ALIAS:
+ case LLDP_PORT_SUBTYPE_INTERFACE_NAME:
+ case LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED:
+
+ r = tlv_packet_read_string(tlv, &s, length);
+ if (r < 0)
+ goto out;
+
+ *data = (uint8_t *) s;
+
+ break;
+ case LLDP_PORT_SUBTYPE_MAC_ADDRESS:
+
+ r = tlv_packet_read_bytes(tlv, data, length);
+ if (r < 0)
+ goto out;
+
+ break;
+ default:
+ r = -EOPNOTSUPP;
+ break;
+ }
+
+ *type = subtype;
+
+ out:
+ r2 = lldp_tlv_packet_exit_container(tlv);
+
+ return r < 0 ? r : r2;
+}
+
+int sd_lldp_packet_read_ttl(tlv_packet *tlv, uint16_t *ttl) {
+ return lldp_tlv_packet_read_u16_tlv(tlv, LLDP_TYPE_TTL, ttl);
+}
+
+int sd_lldp_packet_read_system_name(tlv_packet *tlv,
+ char **data,
+ uint16_t *length) {
+ return lldp_tlv_packet_read_string_tlv(tlv, LLDP_TYPE_SYSTEM_NAME, data, length);
+}
+
+int sd_lldp_packet_read_system_description(tlv_packet *tlv,
+ char **data,
+ uint16_t *length) {
+ return lldp_tlv_packet_read_string_tlv(tlv, LLDP_TYPE_SYSTEM_DESCRIPTION, data, length);
+}
+
+int sd_lldp_packet_read_port_description(tlv_packet *tlv,
+ char **data,
+ uint16_t *length) {
+ return lldp_tlv_packet_read_string_tlv(tlv, LLDP_TYPE_PORT_DESCRIPTION, data, length);
+}
+
+int sd_lldp_packet_read_system_capability(tlv_packet *tlv, uint16_t *data) {
+ return lldp_tlv_packet_read_u16_tlv(tlv, LLDP_TYPE_SYSTEM_CAPABILITIES, data);
+}
+
+int sd_lldp_packet_read_port_vlan_id(tlv_packet *tlv, uint16_t *id) {
+ int r, r2;
+
+ assert_return(tlv, -EINVAL);
+
+ r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_PORT_VLAN_ID);
+ if (r < 0)
+ return r;
+
+ r = tlv_packet_read_u16(tlv, id);
+ r2 = lldp_tlv_packet_exit_container(tlv);
+
+ return r < 0 ? r : r2;
+}
+
+int sd_lldp_packet_read_port_protocol_vlan_id(sd_lldp_packet *tlv, uint8_t *flags, uint16_t *id) {
+ int r, r2;
+
+ assert_return(tlv, -EINVAL);
+
+ r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_PORT_PROTOCOL_VLAN_ID);
+ if (r < 0)
+ return r;
+
+ r = tlv_packet_read_u8(tlv, flags);
+ if (r >= 0)
+ r = tlv_packet_read_u16(tlv, id);
+
+ r2 = lldp_tlv_packet_exit_container(tlv);
+
+ return r < 0 ? r : r2;
+}
+
+int sd_lldp_packet_read_vlan_name(tlv_packet *tlv, uint16_t *vlan_id, char **name, uint16_t *length) {
+ int r, r2;
+ uint8_t len = 0;
+
+ assert_return(tlv, -EINVAL);
+
+ r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_VLAN_NAME);
+ if (r < 0)
+ return r;
+
+ r = tlv_packet_read_u16(tlv, vlan_id);
+ if (r >= 0)
+ r = tlv_packet_read_u8(tlv, &len);
+ if (r >= 0)
+ r = tlv_packet_read_string(tlv, name, length);
+
+ if (r >= 0 && len < *length)
+ *length = len;
+
+ r2 = lldp_tlv_packet_exit_container(tlv);
+
+ return r < 0 ? r : r2;
+}
+
+int sd_lldp_packet_read_management_vid(tlv_packet *tlv, uint16_t *id) {
+ int r, r2;
+
+ assert_return(tlv, -EINVAL);
+
+ r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_MANAGEMENT_VID);
+ if (r < 0)
+ return r;
+
+ r = tlv_packet_read_u16(tlv, id);
+ r2 = lldp_tlv_packet_exit_container(tlv);
+
+ return r < 0 ? r : r2;
+}
+
+int sd_lldp_packet_read_link_aggregation(sd_lldp_packet *tlv, uint8_t *status, uint32_t *id) {
+ int r, r2;
+
+ assert_return(tlv, -EINVAL);
+
+ r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_LINK_AGGREGATION);
+ if (r < 0)
+ return r;
+
+ r = tlv_packet_read_u8(tlv, status);
+ if (r >= 0)
+ r = tlv_packet_read_u32(tlv, id);
+
+ r2 = lldp_tlv_packet_exit_container(tlv);
+
+ return r < 0 ? r : r2;
+}
+
+int sd_lldp_packet_get_destination_type(tlv_packet *tlv, int *dest) {
+ assert_return(tlv, -EINVAL);
+ assert_return(dest, -EINVAL);
+
+ /* 802.1AB-2009, Table 7-1 */
+ if (!memcmp(&tlv->mac, LLDP_MAC_NEAREST_BRIDGE, ETH_ALEN))
+ *dest = SD_LLDP_DESTINATION_TYPE_NEAREST_BRIDGE;
+ else if (!memcmp(&tlv->mac, LLDP_MAC_NEAREST_NON_TPMR_BRIDGE, ETH_ALEN))
+ *dest = SD_LLDP_DESTINATION_TYPE_NEAREST_NON_TPMR_BRIDGE;
+ else if (!memcmp(&tlv->mac, LLDP_MAC_NEAREST_CUSTOMER_BRIDGE, ETH_ALEN))
+ *dest = SD_LLDP_DESTINATION_TYPE_NEAREST_CUSTOMER_BRIDGE;
+ else
+ return -EINVAL;
+
+ return 0;
+}
diff --git a/src/libsystemd-network/lldp-tlv.h b/src/libsystemd-network/lldp-tlv.h
index ce3334e115..744dec37f7 100644
--- a/src/libsystemd-network/lldp-tlv.h
+++ b/src/libsystemd-network/lldp-tlv.h
@@ -24,16 +24,22 @@
#include <net/ethernet.h>
-#include "util.h"
-#include "lldp.h"
+#include "sd-lldp.h"
+
#include "list.h"
+#include "lldp.h"
+#include "util.h"
-typedef struct tlv_packet tlv_packet;
-typedef struct tlv_section tlv_section;
+typedef struct sd_lldp_packet tlv_packet;
+typedef struct sd_lldp_section tlv_section;
-struct tlv_section {
+#define LLDP_OUI_LEN 3
+
+struct sd_lldp_section {
uint16_t type;
uint16_t length;
+ uint8_t *oui;
+ uint8_t subtype;
uint8_t *read_pos;
uint8_t *data;
@@ -41,10 +47,16 @@ struct tlv_section {
LIST_FIELDS(tlv_section, section);
};
+#define LLDP_MAC_NEAREST_BRIDGE (uint8_t[]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }
+#define LLDP_MAC_NEAREST_NON_TPMR_BRIDGE (uint8_t[]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }
+#define LLDP_MAC_NEAREST_CUSTOMER_BRIDGE (uint8_t[]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }
+
int tlv_section_new(tlv_section **ret);
void tlv_section_free(tlv_section *ret);
-struct tlv_packet {
+struct sd_lldp_packet {
+ unsigned n_ref;
+
uint16_t type;
uint16_t length;
usec_t ts;
@@ -61,10 +73,6 @@ struct tlv_packet {
};
int tlv_packet_new(tlv_packet **ret);
-void tlv_packet_free(tlv_packet *m);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(tlv_packet*, tlv_packet_free);
-#define _cleanup_tlv_packet_free_ _cleanup_(tlv_packet_freep)
int lldp_tlv_packet_open_container(tlv_packet *m, uint16_t type);
int lldp_tlv_packet_close_container(tlv_packet *m);
@@ -76,6 +84,7 @@ int tlv_packet_append_u32(tlv_packet *m, uint32_t data);
int tlv_packet_append_string(tlv_packet *m, char *data, uint16_t size);
int lldp_tlv_packet_enter_container(tlv_packet *m, uint16_t type);
+int lldp_tlv_packet_enter_container_oui(tlv_packet *m, const uint8_t *oui, uint8_t subtype);
int lldp_tlv_packet_exit_container(tlv_packet *m);
int tlv_packet_read_bytes(tlv_packet *m, uint8_t **data, uint16_t *data_length);
diff --git a/src/libsystemd-network/lldp.h b/src/libsystemd-network/lldp.h
index 5e4b283e26..19e5cc5f41 100644
--- a/src/libsystemd-network/lldp.h
+++ b/src/libsystemd-network/lldp.h
@@ -113,3 +113,16 @@ typedef enum LLDPMedCapability {
LLDP_MED_CAPABILITY_MAX,
LLDP_MED_CAPABILITY_INVALID = -1,
} LLDPMedCapability;
+
+#define LLDP_OUI_802_1 (uint8_t[]) { 0x00, 0x80, 0xc2 }
+#define LLDP_OUI_802_3 (uint8_t[]) { 0x00, 0x12, 0x0f }
+
+enum {
+ LLDP_OUI_SUBTYPE_802_1_PORT_VLAN_ID = 1,
+ LLDP_OUI_SUBTYPE_802_1_PORT_PROTOCOL_VLAN_ID = 2,
+ LLDP_OUI_SUBTYPE_802_1_VLAN_NAME = 3,
+ LLDP_OUI_SUBTYPE_802_1_PROTOCOL_IDENTITY = 4,
+ LLDP_OUI_SUBTYPE_802_1_VID_USAGE_DIGEST = 5,
+ LLDP_OUI_SUBTYPE_802_1_MANAGEMENT_VID = 6,
+ LLDP_OUI_SUBTYPE_802_1_LINK_AGGREGATION = 7,
+};
diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c
index fab4ddbde4..5da06435ed 100644
--- a/src/libsystemd-network/network-internal.c
+++ b/src/libsystemd-network/network-internal.c
@@ -19,20 +19,25 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <netinet/ether.h>
-#include <linux/if.h>
#include <arpa/inet.h>
+#include <linux/if.h>
+#include <netinet/ether.h>
-#include "strv.h"
-#include "siphash24.h"
+#include "sd-ndisc.h"
+
+#include "alloc-util.h"
+#include "condition.h"
+#include "conf-parser.h"
#include "dhcp-lease-internal.h"
+#include "hexdecoct.h"
#include "log.h"
+#include "network-internal.h"
+#include "parse-util.h"
+#include "siphash24.h"
+#include "string-util.h"
+#include "strv.h"
#include "utf8.h"
#include "util.h"
-#include "conf-parser.h"
-#include "condition.h"
-#include "network-internal.h"
-#include "sd-icmp6-nd.h"
const char *net_get_name(struct udev_device *device) {
const char *name, *field;
@@ -51,7 +56,7 @@ const char *net_get_name(struct udev_device *device) {
#define HASH_KEY SD_ID128_MAKE(d3,1e,48,fa,90,fe,4b,4c,9d,af,d5,d7,a1,b1,2e,8a)
-int net_get_unique_predictable_data(struct udev_device *device, uint8_t result[8]) {
+int net_get_unique_predictable_data(struct udev_device *device, uint64_t *result) {
size_t l, sz = 0;
const char *name = NULL;
int r;
@@ -76,7 +81,7 @@ int net_get_unique_predictable_data(struct udev_device *device, uint8_t result[8
/* Let's hash the machine ID plus the device name. We
* use a fixed, but originally randomly created hash
* key here. */
- siphash24(result, v, sz, HASH_KEY.bytes);
+ *result = htole64(siphash24(v, sz, HASH_KEY.bytes));
return 0;
}
@@ -196,8 +201,7 @@ int config_parse_ifname(const char *unit,
return log_oom();
if (!ascii_is_valid(n) || strlen(n) >= IFNAMSIZ) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Interface name is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
return 0;
}
@@ -240,8 +244,7 @@ int config_parse_ifnames(const char *unit,
return log_oom();
if (!ascii_is_valid(n) || strlen(n) >= IFNAMSIZ) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Interface name is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
free(n);
return 0;
}
@@ -278,8 +281,7 @@ int config_parse_ifalias(const char *unit,
return log_oom();
if (!ascii_is_valid(n) || strlen(n) >= IFALIASZ) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Interface alias is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Interface alias is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
return 0;
}
@@ -324,8 +326,7 @@ int config_parse_hwaddr(const char *unit,
&n->ether_addr_octet[4],
&n->ether_addr_octet[5]);
if (r != 6) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Not a valid MAC address, ignoring assignment: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Not a valid MAC address, ignoring assignment: %s", rvalue);
free(n);
return 0;
}
@@ -394,8 +395,8 @@ void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses,
assert(size);
for (i = 0; i < size; i++)
- fprintf(f, SD_ICMP6_ND_ADDRESS_FORMAT_STR"%s",
- SD_ICMP6_ND_ADDRESS_FORMAT_VAL(addresses[i]),
+ fprintf(f, SD_NDISC_ADDRESS_FORMAT_STR"%s",
+ SD_NDISC_ADDRESS_FORMAT_VAL(addresses[i]),
(i < (size - 1)) ? " ": "");
}
@@ -436,7 +437,7 @@ int deserialize_in6_addrs(struct in6_addr **ret, const char *string) {
return size;
}
-void serialize_dhcp_routes(FILE *f, const char *key, struct sd_dhcp_route *routes, size_t size) {
+void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, size_t size) {
unsigned i;
assert(f);
@@ -447,10 +448,15 @@ void serialize_dhcp_routes(FILE *f, const char *key, struct sd_dhcp_route *route
fprintf(f, "%s=", key);
for (i = 0; i < size; i++) {
- fprintf(f, "%s/%" PRIu8, inet_ntoa(routes[i].dst_addr),
- routes[i].dst_prefixlen);
- fprintf(f, ",%s%s", inet_ntoa(routes[i].gw_addr),
- (i < (size - 1)) ? " ": "");
+ struct in_addr dest, gw;
+ uint8_t length;
+
+ assert_se(sd_dhcp_route_get_destination(routes[i], &dest) >= 0);
+ assert_se(sd_dhcp_route_get_gateway(routes[i], &gw) >= 0);
+ assert_se(sd_dhcp_route_get_destination_prefix_length(routes[i], &length) >= 0);
+
+ fprintf(f, "%s/%" PRIu8, inet_ntoa(dest), length);
+ fprintf(f, ",%s%s", inet_ntoa(gw), (i < (size - 1)) ? " ": "");
}
fputs("\n", f);
diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h
index d5d4ef42f2..c43c01accf 100644
--- a/src/libsystemd-network/network-internal.h
+++ b/src/libsystemd-network/network-internal.h
@@ -23,8 +23,10 @@
#include <stdbool.h>
-#include "udev.h"
+#include "sd-dhcp-lease.h"
+
#include "condition.h"
+#include "udev.h"
bool net_match_config(const struct ether_addr *match_mac,
char * const *match_path,
@@ -62,7 +64,7 @@ int config_parse_ifalias(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 net_get_unique_predictable_data(struct udev_device *device, uint8_t result[8]);
+int net_get_unique_predictable_data(struct udev_device *device, uint64_t *result);
const char *net_get_name(struct udev_device *device);
void serialize_in_addrs(FILE *f, const struct in_addr *addresses, size_t size);
@@ -74,7 +76,7 @@ int deserialize_in6_addrs(struct in6_addr **addresses, const char *string);
/* don't include "dhcp-lease-internal.h" as it causes conflicts between netinet/ip.h and linux/ip.h */
struct sd_dhcp_route;
-void serialize_dhcp_routes(FILE *f, const char *key, struct sd_dhcp_route *routes, size_t size);
+void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, size_t size);
int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t *ret_allocated, const char *string);
int serialize_dhcp_option(FILE *f, const char *key, const void *data, size_t size);
diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c
index 141b836a0d..d3ad6b7717 100644
--- a/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/libsystemd-network/sd-dhcp-client.c
@@ -17,24 +17,28 @@
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 <linux/if_infiniband.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <sys/ioctl.h>
+#include <linux/if_infiniband.h>
-#include "util.h"
-#include "random-util.h"
-#include "async.h"
+#include "sd-dhcp-client.h"
-#include "dhcp-protocol.h"
+#include "alloc-util.h"
+#include "async.h"
+#include "dhcp-identifier.h"
#include "dhcp-internal.h"
#include "dhcp-lease-internal.h"
-#include "dhcp-identifier.h"
-#include "sd-dhcp-client.h"
+#include "dhcp-protocol.h"
+#include "dns-domain.h"
+#include "hostname-util.h"
+#include "random-util.h"
+#include "string-util.h"
+#include "util.h"
#define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN) /* Arbitrary limit */
#define MAX_MAC_ADDR_LEN CONST_MAX(INFINIBAND_ALEN, ETH_ALEN)
@@ -100,11 +104,11 @@ struct sd_dhcp_client {
};
static const uint8_t default_req_opts[] = {
- DHCP_OPTION_SUBNET_MASK,
- DHCP_OPTION_ROUTER,
- DHCP_OPTION_HOST_NAME,
- DHCP_OPTION_DOMAIN_NAME,
- DHCP_OPTION_DOMAIN_NAME_SERVER,
+ SD_DHCP_OPTION_SUBNET_MASK,
+ SD_DHCP_OPTION_ROUTER,
+ SD_DHCP_OPTION_HOST_NAME,
+ SD_DHCP_OPTION_DOMAIN_NAME,
+ SD_DHCP_OPTION_DOMAIN_NAME_SERVER,
};
static int client_receive_message_raw(sd_event_source *s, int fd,
@@ -139,11 +143,11 @@ int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
DHCP_STATE_STOPPED), -EBUSY);
switch(option) {
- case DHCP_OPTION_PAD:
- case DHCP_OPTION_OVERLOAD:
- case DHCP_OPTION_MESSAGE_TYPE:
- case DHCP_OPTION_PARAMETER_REQUEST_LIST:
- case DHCP_OPTION_END:
+ case SD_DHCP_OPTION_PAD:
+ case SD_DHCP_OPTION_OVERLOAD:
+ case SD_DHCP_OPTION_MESSAGE_TYPE:
+ case SD_DHCP_OPTION_PARAMETER_REQUEST_LIST:
+ case SD_DHCP_OPTION_END:
return -EINVAL;
default:
@@ -296,6 +300,9 @@ int sd_dhcp_client_set_hostname(sd_dhcp_client *client,
assert_return(client, -EINVAL);
+ if (!hostname_is_valid(hostname, false) && !dns_name_is_valid(hostname))
+ return -EINVAL;
+
if (streq_ptr(client->hostname, hostname))
return 0;
@@ -479,7 +486,7 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
Identifier option is not set */
if (client->client_id_len) {
r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_CLIENT_IDENTIFIER,
+ SD_DHCP_OPTION_CLIENT_IDENTIFIER,
client->client_id_len,
&client->client_id);
if (r < 0)
@@ -495,7 +502,7 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
messages.
*/
r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_PARAMETER_REQUEST_LIST,
+ SD_DHCP_OPTION_PARAMETER_REQUEST_LIST,
client->req_opts_size, client->req_opts);
if (r < 0)
return r;
@@ -524,7 +531,7 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
*/
max_size = htobe16(size);
r = dhcp_option_append(&packet->dhcp, client->mtu, &optoffset, 0,
- DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
+ SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
2, &max_size);
if (r < 0)
return r;
@@ -537,6 +544,24 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
return 0;
}
+static int client_append_fqdn_option(DHCPMessage *message, size_t optlen, size_t *optoffset,
+ const char *fqdn) {
+ uint8_t buffer[3 + DHCP_MAX_FQDN_LENGTH];
+ int r;
+
+ buffer[0] = DHCP_FQDN_FLAG_S | /* Request server to perform A RR DNS updates */
+ DHCP_FQDN_FLAG_E; /* Canonical wire format */
+ buffer[1] = 0; /* RCODE1 (deprecated) */
+ buffer[2] = 0; /* RCODE2 (deprecated) */
+
+ r = dns_name_to_wire_format(fqdn, buffer + 3, sizeof(buffer) - 3, false);
+ if (r > 0)
+ r = dhcp_option_append(message, optlen, optoffset, 0,
+ SD_DHCP_OPTION_FQDN, 3 + r, buffer);
+
+ return r;
+}
+
static int dhcp_client_send_raw(sd_dhcp_client *client, DHCPPacket *packet,
size_t len) {
dhcp_packet_append_ip_headers(packet, INADDR_ANY, DHCP_PORT_CLIENT,
@@ -568,26 +593,34 @@ static int client_send_discover(sd_dhcp_client *client) {
*/
if (client->last_addr != INADDR_ANY) {
r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_REQUESTED_IP_ADDRESS,
+ SD_DHCP_OPTION_REQUESTED_IP_ADDRESS,
4, &client->last_addr);
if (r < 0)
return r;
}
- /* it is unclear from RFC 2131 if client should send hostname in
- DHCPDISCOVER but dhclient does and so we do as well
- */
if (client->hostname) {
- r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_HOST_NAME,
- strlen(client->hostname), client->hostname);
+ /* According to RFC 4702 "clients that send the Client FQDN option in
+ their messages MUST NOT also send the Host Name option". Just send
+ one of the two depending on the hostname type.
+ */
+ if (dns_name_is_single_label(client->hostname)) {
+ /* it is unclear from RFC 2131 if client should send hostname in
+ DHCPDISCOVER but dhclient does and so we do as well
+ */
+ r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+ SD_DHCP_OPTION_HOST_NAME,
+ strlen(client->hostname), client->hostname);
+ } else
+ r = client_append_fqdn_option(&discover->dhcp, optlen, &optoffset,
+ client->hostname);
if (r < 0)
return r;
}
if (client->vendor_class_identifier) {
r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_VENDOR_CLASS_IDENTIFIER,
+ SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER,
strlen(client->vendor_class_identifier),
client->vendor_class_identifier);
if (r < 0)
@@ -595,7 +628,7 @@ static int client_send_discover(sd_dhcp_client *client) {
}
r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_END, 0, NULL);
+ SD_DHCP_OPTION_END, 0, NULL);
if (r < 0)
return r;
@@ -634,13 +667,13 @@ static int client_send_request(sd_dhcp_client *client) {
*/
r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_SERVER_IDENTIFIER,
+ SD_DHCP_OPTION_SERVER_IDENTIFIER,
4, &client->lease->server_address);
if (r < 0)
return r;
r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_REQUESTED_IP_ADDRESS,
+ SD_DHCP_OPTION_REQUESTED_IP_ADDRESS,
4, &client->lease->address);
if (r < 0)
return r;
@@ -653,7 +686,7 @@ static int client_send_request(sd_dhcp_client *client) {
assigned address. ’ciaddr’ MUST be zero.
*/
r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_REQUESTED_IP_ADDRESS,
+ SD_DHCP_OPTION_REQUESTED_IP_ADDRESS,
4, &client->last_addr);
if (r < 0)
return r;
@@ -686,15 +719,19 @@ static int client_send_request(sd_dhcp_client *client) {
}
if (client->hostname) {
- r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_HOST_NAME,
- strlen(client->hostname), client->hostname);
+ if (dns_name_is_single_label(client->hostname))
+ r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
+ SD_DHCP_OPTION_HOST_NAME,
+ strlen(client->hostname), client->hostname);
+ else
+ r = client_append_fqdn_option(&request->dhcp, optlen, &optoffset,
+ client->hostname);
if (r < 0)
return r;
}
r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_END, 0, NULL);
+ SD_DHCP_OPTION_END, 0, NULL);
if (r < 0)
return r;
@@ -1030,7 +1067,7 @@ static int client_timeout_t1(sd_event_source *s, uint64_t usec,
static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
size_t len) {
- _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
+ _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
int r;
r = dhcp_lease_new(&lease);
@@ -1045,7 +1082,7 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
return r;
}
- r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease);
+ r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease, NULL);
if (r != DHCP_OFFER) {
log_dhcp_client(client, "received message was not an OFFER, ignoring");
return -ENOMSG;
@@ -1084,7 +1121,7 @@ static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force,
size_t len) {
int r;
- r = dhcp_option_parse(force, len, NULL, NULL);
+ r = dhcp_option_parse(force, len, NULL, NULL, NULL);
if (r != DHCP_FORCERENEW)
return -ENOMSG;
@@ -1095,7 +1132,8 @@ static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force,
static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
size_t len) {
- _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
+ _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
+ _cleanup_free_ char *error_message = NULL;
int r;
r = dhcp_lease_new(&lease);
@@ -1110,9 +1148,9 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
return r;
}
- r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease);
+ r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease, &error_message);
if (r == DHCP_NAK) {
- log_dhcp_client(client, "NAK");
+ log_dhcp_client(client, "NAK: %s", strna(error_message));
return -EADDRNOTAVAIL;
}
@@ -1265,8 +1303,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) {
return r;
log_dhcp_client(client, "lease expires in %s",
- format_timespan(time_string, FORMAT_TIMESPAN_MAX,
- lifetime_timeout - time_now, 0));
+ format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_timeout - time_now, USEC_PER_SEC));
/* don't arm earlier timeouts if this has already expired */
if (lifetime_timeout <= time_now)
@@ -1292,8 +1329,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) {
return r;
log_dhcp_client(client, "T2 expires in %s",
- format_timespan(time_string, FORMAT_TIMESPAN_MAX,
- t2_timeout - time_now, 0));
+ format_timespan(time_string, FORMAT_TIMESPAN_MAX, t2_timeout - time_now, USEC_PER_SEC));
/* don't arm earlier timeout if this has already expired */
if (t2_timeout <= time_now)
@@ -1318,8 +1354,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) {
return r;
log_dhcp_client(client, "T1 expires in %s",
- format_timespan(time_string, FORMAT_TIMESPAN_MAX,
- t1_timeout - time_now, 0));
+ format_timespan(time_string, FORMAT_TIMESPAN_MAX, t1_timeout - time_now, USEC_PER_SEC));
return 0;
}
@@ -1479,9 +1514,8 @@ static int client_receive_message_udp(sd_event_source *s, int fd,
r = ioctl(fd, FIONREAD, &buflen);
if (r < 0)
- return r;
-
- if (buflen < 0)
+ return -errno;
+ else if (buflen < 0)
/* this can't be right */
return -EIO;
@@ -1491,26 +1525,28 @@ static int client_receive_message_udp(sd_event_source *s, int fd,
len = read(fd, message, buflen);
if (len < 0) {
- log_dhcp_client(client, "could not receive message from UDP "
- "socket: %m");
- return 0;
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
+
+ log_dhcp_client(client, "Could not receive message from UDP socket: %m");
+ return -errno;
} else if ((size_t)len < sizeof(DHCPMessage)) {
- log_dhcp_client(client, "too small to be a DHCP message: ignoring");
+ log_dhcp_client(client, "Too small to be a DHCP message: ignoring");
return 0;
}
if (be32toh(message->magic) != DHCP_MAGIC_COOKIE) {
- log_dhcp_client(client, "not a DHCP message: ignoring");
+ log_dhcp_client(client, "Not a DHCP message: ignoring");
return 0;
}
if (message->op != BOOTREPLY) {
- log_dhcp_client(client, "not a BOOTREPLY message: ignoring");
+ log_dhcp_client(client, "Not a BOOTREPLY message: ignoring");
return 0;
}
if (message->htype != client->arp_type) {
- log_dhcp_client(client, "packet type does not match client type");
+ log_dhcp_client(client, "Packet type does not match client type");
return 0;
}
@@ -1518,19 +1554,18 @@ static int client_receive_message_udp(sd_event_source *s, int fd,
expected_hlen = ETH_ALEN;
expected_chaddr = (const struct ether_addr *) &client->mac_addr;
} else {
- /* Non-ethernet links expect zero chaddr */
+ /* Non-Ethernet links expect zero chaddr */
expected_hlen = 0;
expected_chaddr = &zero_mac;
}
if (message->hlen != expected_hlen) {
- log_dhcp_client(client, "unexpected packet hlen %d", message->hlen);
+ log_dhcp_client(client, "Unexpected packet hlen %d", message->hlen);
return 0;
}
if (memcmp(&message->chaddr[0], expected_chaddr, ETH_ALEN)) {
- log_dhcp_client(client, "received chaddr does not match "
- "expected: ignoring");
+ log_dhcp_client(client, "Received chaddr does not match expected: ignoring");
return 0;
}
@@ -1538,8 +1573,7 @@ static int client_receive_message_udp(sd_event_source *s, int fd,
be32toh(message->xid) != client->xid) {
/* in BOUND state, we may receive FORCERENEW with xid set by server,
so ignore the xid in this case */
- log_dhcp_client(client, "received xid (%u) does not match "
- "expected (%u): ignoring",
+ log_dhcp_client(client, "Received xid (%u) does not match expected (%u): ignoring",
be32toh(message->xid), client->xid);
return 0;
}
@@ -1568,9 +1602,8 @@ static int client_receive_message_raw(sd_event_source *s, int fd,
r = ioctl(fd, FIONREAD, &buflen);
if (r < 0)
- return r;
-
- if (buflen < 0)
+ return -errno;
+ else if (buflen < 0)
/* this can't be right */
return -EIO;
@@ -1583,9 +1616,12 @@ static int client_receive_message_raw(sd_event_source *s, int fd,
len = recvmsg(fd, &msg, 0);
if (len < 0) {
- log_dhcp_client(client, "could not receive message from raw "
- "socket: %m");
- return 0;
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
+
+ log_dhcp_client(client, "Could not receive message from raw socket: %m");
+
+ return -errno;
} else if ((size_t)len < sizeof(DHCPPacket))
return 0;
@@ -1715,7 +1751,7 @@ sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) {
}
int sd_dhcp_client_new(sd_dhcp_client **ret) {
- _cleanup_dhcp_client_unref_ sd_dhcp_client *client = NULL;
+ _cleanup_(sd_dhcp_client_unrefp) sd_dhcp_client *client = NULL;
assert_return(ret, -EINVAL);
diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c
index aa07846693..f466b07503 100644
--- a/src/libsystemd-network/sd-dhcp-lease.c
+++ b/src/libsystemd-network/sd-dhcp-lease.c
@@ -18,21 +18,28 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
+#include <arpa/inet.h>
#include <errno.h>
-#include <string.h>
#include <stdio.h>
-#include <arpa/inet.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "sd-dhcp-lease.h"
+#include "alloc-util.h"
+#include "dhcp-lease-internal.h"
+#include "dhcp-protocol.h"
+#include "dns-domain.h"
+#include "fd-util.h"
#include "fileio.h"
-#include "unaligned.h"
-#include "in-addr-util.h"
+#include "hexdecoct.h"
#include "hostname-util.h"
-#include "dns-domain.h"
+#include "in-addr-util.h"
#include "network-internal.h"
-#include "dhcp-protocol.h"
-#include "dhcp-lease-internal.h"
-#include "sd-dhcp-lease.h"
+#include "parse-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
+#include "unaligned.h"
int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) {
assert_return(lease, -EINVAL);
@@ -199,14 +206,28 @@ int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr) {
return 0;
}
-int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, struct sd_dhcp_route **routes) {
+/*
+ * The returned routes array must be freed by the caller.
+ * Route objects have the same lifetime of the lease and must not be freed.
+ */
+int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes) {
+ sd_dhcp_route **ret;
+ unsigned i;
+
assert_return(lease, -EINVAL);
assert_return(routes, -EINVAL);
if (lease->static_route_size <= 0)
return -ENODATA;
- *routes = lease->static_route;
+ ret = new(sd_dhcp_route *, lease->static_route_size);
+ if (!ret)
+ return -ENOMEM;
+
+ for (i = 0; i < lease->static_route_size; i++)
+ ret[i] = &lease->static_route[i];
+
+ *routes = ret;
return (int) lease->static_route_size;
}
@@ -314,10 +335,14 @@ static int lease_parse_string(const uint8_t *option, size_t len, char **ret) {
else {
char *string;
- if (memchr(option, 0, len))
+ /*
+ * One trailing NUL byte is OK, we don't mind. See:
+ * https://github.com/systemd/systemd/issues/1337
+ */
+ if (memchr(option, 0, len - 1))
return -EINVAL;
- string = strndup((const char *)option, len);
+ string = strndup((const char *) option, len);
if (!string)
return -ENOMEM;
@@ -442,7 +467,7 @@ static int lease_parse_classless_routes(
if (len < 4)
return -EINVAL;
- lease_parse_be32(option, 4, &route->gw_addr.s_addr);
+ assert_se(lease_parse_be32(option, 4, &route->gw_addr.s_addr) >= 0);
option += 4;
len -= 4;
@@ -460,21 +485,21 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
switch(code) {
- case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
+ case SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
r = lease_parse_u32(option, len, &lease->lifetime, 1);
if (r < 0)
log_debug_errno(r, "Failed to parse lease time, ignoring: %m");
break;
- case DHCP_OPTION_SERVER_IDENTIFIER:
+ case SD_DHCP_OPTION_SERVER_IDENTIFIER:
r = lease_parse_be32(option, len, &lease->server_address);
if (r < 0)
log_debug_errno(r, "Failed to parse server identifier, ignoring: %m");
break;
- case DHCP_OPTION_SUBNET_MASK:
+ case SD_DHCP_OPTION_SUBNET_MASK:
r = lease_parse_be32(option, len, &lease->subnet_mask);
if (r < 0)
log_debug_errno(r, "Failed to parse subnet mask, ignoring: %m");
@@ -482,7 +507,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
lease->have_subnet_mask = true;
break;
- case DHCP_OPTION_BROADCAST:
+ case SD_DHCP_OPTION_BROADCAST:
r = lease_parse_be32(option, len, &lease->broadcast);
if (r < 0)
log_debug_errno(r, "Failed to parse broadcast address, ignoring: %m");
@@ -490,7 +515,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
lease->have_broadcast = true;
break;
- case DHCP_OPTION_ROUTER:
+ case SD_DHCP_OPTION_ROUTER:
if (len >= 4) {
r = lease_parse_be32(option, 4, &lease->router);
if (r < 0)
@@ -498,31 +523,31 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
}
break;
- case DHCP_OPTION_DOMAIN_NAME_SERVER:
+ case SD_DHCP_OPTION_DOMAIN_NAME_SERVER:
r = lease_parse_in_addrs(option, len, &lease->dns, &lease->dns_size);
if (r < 0)
log_debug_errno(r, "Failed to parse DNS server, ignoring: %m");
break;
- case DHCP_OPTION_NTP_SERVER:
+ case SD_DHCP_OPTION_NTP_SERVER:
r = lease_parse_in_addrs(option, len, &lease->ntp, &lease->ntp_size);
if (r < 0)
log_debug_errno(r, "Failed to parse NTP server, ignoring: %m");
break;
- case DHCP_OPTION_STATIC_ROUTE:
+ case SD_DHCP_OPTION_STATIC_ROUTE:
r = lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size, &lease->static_route_allocated);
if (r < 0)
log_debug_errno(r, "Failed to parse static routes, ignoring: %m");
break;
- case DHCP_OPTION_INTERFACE_MTU:
+ case SD_DHCP_OPTION_INTERFACE_MTU:
r = lease_parse_u16(option, len, &lease->mtu, 68);
if (r < 0)
log_debug_errno(r, "Failed to parse MTU, ignoring: %m");
break;
- case DHCP_OPTION_DOMAIN_NAME: {
+ case SD_DHCP_OPTION_DOMAIN_NAME: {
_cleanup_free_ char *domainname = NULL, *normalized = NULL;
r = lease_parse_string(option, len, &domainname);
@@ -549,7 +574,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
break;
}
- case DHCP_OPTION_HOST_NAME: {
+ case SD_DHCP_OPTION_HOST_NAME: {
_cleanup_free_ char *hostname = NULL, *normalized = NULL;
r = lease_parse_string(option, len, &hostname);
@@ -576,25 +601,25 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
break;
}
- case DHCP_OPTION_ROOT_PATH:
+ case SD_DHCP_OPTION_ROOT_PATH:
r = lease_parse_string(option, len, &lease->root_path);
if (r < 0)
log_debug_errno(r, "Failed to parse root path, ignoring: %m");
break;
- case DHCP_OPTION_RENEWAL_T1_TIME:
+ case SD_DHCP_OPTION_RENEWAL_T1_TIME:
r = lease_parse_u32(option, len, &lease->t1, 1);
if (r < 0)
log_debug_errno(r, "Failed to parse T1 time, ignoring: %m");
break;
- case DHCP_OPTION_REBINDING_T2_TIME:
+ case SD_DHCP_OPTION_REBINDING_T2_TIME:
r = lease_parse_u32(option, len, &lease->t2, 1);
if (r < 0)
log_debug_errno(r, "Failed to parse T2 time, ignoring: %m");
break;
- case DHCP_OPTION_CLASSLESS_STATIC_ROUTE:
+ case SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE:
r = lease_parse_classless_routes(
option, len,
&lease->static_route,
@@ -604,7 +629,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
log_debug_errno(r, "Failed to parse classless routes, ignoring: %m");
break;
- case DHCP_OPTION_NEW_TZDB_TIMEZONE: {
+ case SD_DHCP_OPTION_NEW_TZDB_TIMEZONE: {
_cleanup_free_ char *tz = NULL;
r = lease_parse_string(option, len, &tz);
@@ -625,7 +650,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
break;
}
- case DHCP_OPTION_VENDOR_SPECIFIC:
+ case SD_DHCP_OPTION_VENDOR_SPECIFIC:
if (len <= 0)
lease->vendor_specific = mfree(lease->vendor_specific);
@@ -643,7 +668,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
lease->vendor_specific_len = len;
break;
- case DHCP_OPTION_PRIVATE_BASE ... DHCP_OPTION_PRIVATE_LAST:
+ case SD_DHCP_OPTION_PRIVATE_BASE ... SD_DHCP_OPTION_PRIVATE_LAST:
r = dhcp_lease_insert_private_option(lease, code, option, len);
if (r < 0)
return r;
@@ -651,7 +676,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
break;
default:
- log_debug("Ignoring option DHCP option %i while parsing.", code);
+ log_debug("Ignoring option DHCP option %"PRIu8" while parsing.", code);
break;
}
@@ -712,7 +737,7 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
size_t client_id_len, data_len;
const char *string;
uint16_t mtu;
- struct sd_dhcp_route *routes;
+ _cleanup_free_ sd_dhcp_route **routes = NULL;
uint32_t t1, t2, lifetime;
int r;
@@ -829,7 +854,7 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
LIST_FOREACH(options, option, lease->private_options) {
char key[strlen("OPTION_000")+1];
- snprintf(key, sizeof(key), "OPTION_%"PRIu8, option->tag);
+ xsprintf(key, "OPTION_%" PRIu8, option->tag);
r = serialize_dhcp_option(f, key, option->data, option->length);
if (r < 0)
goto fail;
@@ -855,7 +880,7 @@ fail:
int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
- _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
+ _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
_cleanup_free_ char
*address = NULL,
*router = NULL,
@@ -872,7 +897,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
*lifetime = NULL,
*t1 = NULL,
*t2 = NULL,
- *options[DHCP_OPTION_PRIVATE_LAST - DHCP_OPTION_PRIVATE_BASE + 1] = {};
+ *options[SD_DHCP_OPTION_PRIVATE_LAST - SD_DHCP_OPTION_PRIVATE_BASE + 1] = {};
int r, i;
@@ -941,19 +966,19 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
if (address) {
r = inet_pton(AF_INET, address, &lease->address);
if (r <= 0)
- log_debug_errno(errno, "Failed to parse address %s, ignoring: %m", address);
+ log_debug("Failed to parse address %s, ignoring.", address);
}
if (router) {
r = inet_pton(AF_INET, router, &lease->router);
if (r <= 0)
- log_debug_errno(errno, "Failed to parse router %s, ignoring: %m", router);
+ log_debug("Failed to parse router %s, ignoring.", router);
}
if (netmask) {
r = inet_pton(AF_INET, netmask, &lease->subnet_mask);
if (r <= 0)
- log_debug_errno(errno, "Failed to parse netmask %s, ignoring: %m", netmask);
+ log_debug("Failed to parse netmask %s, ignoring.", netmask);
else
lease->have_subnet_mask = true;
}
@@ -961,19 +986,19 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
if (server_address) {
r = inet_pton(AF_INET, server_address, &lease->server_address);
if (r <= 0)
- log_debug_errno(errno, "Failed to parse netmask %s, ignoring: %m", server_address);
+ log_debug("Failed to parse server address %s, ignoring.", server_address);
}
if (next_server) {
r = inet_pton(AF_INET, next_server, &lease->next_server);
if (r <= 0)
- log_debug_errno(errno, "Failed to parse next server %s, ignoring: %m", next_server);
+ log_debug("Failed to parse next server %s, ignoring.", next_server);
}
if (broadcast) {
r = inet_pton(AF_INET, broadcast, &lease->broadcast);
if (r <= 0)
- log_debug_errno(errno, "Failed to parse broadcast address %s, ignoring: %m", broadcast);
+ log_debug("Failed to parse broadcast address %s, ignoring.", broadcast);
else
lease->have_broadcast = true;
}
@@ -1040,7 +1065,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
log_debug_errno(r, "Failed to parse vendor specific data %s, ignoring: %m", vendor_specific_hex);
}
- for (i = 0; i <= DHCP_OPTION_PRIVATE_LAST - DHCP_OPTION_PRIVATE_BASE; i++) {
+ for (i = 0; i <= SD_DHCP_OPTION_PRIVATE_LAST - SD_DHCP_OPTION_PRIVATE_BASE; i++) {
_cleanup_free_ void *data = NULL;
size_t len;
@@ -1053,7 +1078,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
continue;
}
- r = dhcp_lease_insert_private_option(lease, DHCP_OPTION_PRIVATE_BASE + i, data, len);
+ r = dhcp_lease_insert_private_option(lease, SD_DHCP_OPTION_PRIVATE_BASE + i, data, len);
if (r < 0)
return r;
}
@@ -1131,3 +1156,27 @@ int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **tz) {
*tz = lease->timezone;
return 0;
}
+
+int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *destination) {
+ assert_return(route, -EINVAL);
+ assert_return(destination, -EINVAL);
+
+ *destination = route->dst_addr;
+ return 0;
+}
+
+int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *length) {
+ assert_return(route, -EINVAL);
+ assert_return(length, -EINVAL);
+
+ *length = route->dst_prefixlen;
+ return 0;
+}
+
+int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *gateway) {
+ assert_return(route, -EINVAL);
+ assert_return(gateway, -EINVAL);
+
+ *gateway = route->gw_addr;
+ return 0;
+}
diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c
index 1f167485e3..0b8d4bb843 100644
--- a/src/libsystemd-network/sd-dhcp-server.c
+++ b/src/libsystemd-network/sd-dhcp-server.c
@@ -22,12 +22,15 @@
#include <sys/ioctl.h>
-#include "in-addr-util.h"
-#include "siphash24.h"
-
#include "sd-dhcp-server.h"
-#include "dhcp-server-internal.h"
+
+#include "alloc-util.h"
#include "dhcp-internal.h"
+#include "dhcp-server-internal.h"
+#include "fd-util.h"
+#include "in-addr-util.h"
+#include "siphash24.h"
+#include "string-util.h"
#define DHCP_DEFAULT_LEASE_TIME_USEC USEC_PER_HOUR
#define DHCP_MAX_LEASE_TIME_USEC (USEC_PER_HOUR*12)
@@ -93,7 +96,7 @@ int sd_dhcp_server_configure_pool(sd_dhcp_server *server, struct in_addr *addres
return 0;
}
-bool sd_dhcp_server_is_running(sd_dhcp_server *server) {
+int sd_dhcp_server_is_running(sd_dhcp_server *server) {
assert_return(server, false);
return !!server->receive_message;
@@ -110,18 +113,15 @@ sd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server) {
return server;
}
-unsigned long client_id_hash_func(const void *p,
- const uint8_t hash_key[HASH_KEY_SIZE]) {
- uint64_t u;
+void client_id_hash_func(const void *p, struct siphash *state) {
const DHCPClientId *id = p;
assert(id);
assert(id->length);
assert(id->data);
- siphash24((uint8_t*) &u, id->data, id->length, hash_key);
-
- return (unsigned long) u;
+ siphash24_compress(&id->length, sizeof(id->length), state);
+ siphash24_compress(id->data, id->length, state);
}
int client_id_compare_func(const void *_a, const void *_b) {
@@ -185,7 +185,7 @@ sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) {
}
int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
- _cleanup_dhcp_server_unref_ sd_dhcp_server *server = NULL;
+ _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
assert_return(ret, -EINVAL);
assert_return(ifindex > 0, -EINVAL);
@@ -354,13 +354,13 @@ int dhcp_server_send_packet(sd_dhcp_server *server,
assert(packet);
r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
- DHCP_OPTION_SERVER_IDENTIFIER,
+ SD_DHCP_OPTION_SERVER_IDENTIFIER,
4, &server->address);
if (r < 0)
return r;
r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
- DHCP_OPTION_END, 0, NULL);
+ SD_DHCP_OPTION_END, 0, NULL);
if (r < 0)
return r;
@@ -457,18 +457,18 @@ static int server_send_offer(sd_dhcp_server *server, DHCPRequest *req,
lease_time = htobe32(req->lifetime);
r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
- DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4,
+ SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4,
&lease_time);
if (r < 0)
return r;
r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
- DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
+ SD_DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
if (r < 0)
return r;
r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
- DHCP_OPTION_ROUTER, 4, &server->address);
+ SD_DHCP_OPTION_ROUTER, 4, &server->address);
if (r < 0)
return r;
@@ -494,25 +494,25 @@ static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req,
lease_time = htobe32(req->lifetime);
r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
- DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4,
+ SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4,
&lease_time);
if (r < 0)
return r;
r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
- DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
+ SD_DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
if (r < 0)
return r;
r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
- DHCP_OPTION_ROUTER, 4, &server->address);
+ SD_DHCP_OPTION_ROUTER, 4, &server->address);
if (r < 0)
return r;
if (server->n_dns > 0) {
r = dhcp_option_append(
&packet->dhcp, req->max_optlen, &offset, 0,
- DHCP_OPTION_DOMAIN_NAME_SERVER,
+ SD_DHCP_OPTION_DOMAIN_NAME_SERVER,
sizeof(struct in_addr) * server->n_dns, server->dns);
if (r < 0)
return r;
@@ -521,7 +521,7 @@ static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req,
if (server->n_ntp > 0) {
r = dhcp_option_append(
&packet->dhcp, req->max_optlen, &offset, 0,
- DHCP_OPTION_NTP_SERVER,
+ SD_DHCP_OPTION_NTP_SERVER,
sizeof(struct in_addr) * server->n_ntp, server->ntp);
if (r < 0)
return r;
@@ -530,7 +530,7 @@ static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req,
if (server->timezone) {
r = dhcp_option_append(
&packet->dhcp, req->max_optlen, &offset, 0,
- DHCP_OPTION_NEW_TZDB_TIMEZONE,
+ SD_DHCP_OPTION_NEW_TZDB_TIMEZONE,
strlen(server->timezone), server->timezone);
if (r < 0)
return r;
@@ -576,7 +576,7 @@ static int server_send_forcerenew(sd_dhcp_server *server, be32_t address,
return r;
r = dhcp_option_append(&packet->dhcp, DHCP_MIN_OPTIONS_SIZE,
- &optoffset, 0, DHCP_OPTION_END, 0, NULL);
+ &optoffset, 0, SD_DHCP_OPTION_END, 0, NULL);
if (r < 0)
return r;
@@ -596,22 +596,22 @@ static int parse_request(uint8_t code, uint8_t len, const void *option, void *us
assert(req);
switch(code) {
- case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
+ case SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
if (len == 4)
req->lifetime = be32toh(*(be32_t*)option);
break;
- case DHCP_OPTION_REQUESTED_IP_ADDRESS:
+ case SD_DHCP_OPTION_REQUESTED_IP_ADDRESS:
if (len == 4)
req->requested_ip = *(be32_t*)option;
break;
- case DHCP_OPTION_SERVER_IDENTIFIER:
+ case SD_DHCP_OPTION_SERVER_IDENTIFIER:
if (len == 4)
req->server_id = *(be32_t*)option;
break;
- case DHCP_OPTION_CLIENT_IDENTIFIER:
+ case SD_DHCP_OPTION_CLIENT_IDENTIFIER:
if (len >= 2) {
uint8_t *data;
@@ -625,7 +625,7 @@ static int parse_request(uint8_t code, uint8_t len, const void *option, void *us
}
break;
- case DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
+ case SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
if (len == 2)
req->max_optlen = be16toh(*(be16_t*)option) -
- sizeof(DHCPPacket);
@@ -699,6 +699,7 @@ static int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) {
int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
size_t length) {
_cleanup_dhcp_request_free_ DHCPRequest *req = NULL;
+ _cleanup_free_ char *error_message = NULL;
DHCPLease *existing_lease;
int type, r;
@@ -714,7 +715,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
if (!req)
return -ENOMEM;
- type = dhcp_option_parse(message, length, parse_request, req);
+ type = dhcp_option_parse(message, length, parse_request, req, &error_message);
if (type < 0)
return 0;
@@ -743,13 +744,18 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
if (existing_lease)
address = existing_lease->address;
else {
+ struct siphash state;
+ uint64_t hash;
uint32_t next_offer;
/* even with no persistence of leases, we try to offer the same client
the same IP address. we do this by using the hash of the client id
as the offset into the pool of leases when finding the next free one */
- next_offer = client_id_hash_func(&req->client_id, HASH_KEY.bytes) % server->pool_size;
+ siphash24_init(&state, HASH_KEY.bytes);
+ client_id_hash_func(&req->client_id, &state);
+ hash = htole64(siphash24_finalize(&state));
+ next_offer = hash % server->pool_size;
for (i = 0; i < server->pool_size; i++) {
if (!server->bound_leases[next_offer]) {
@@ -779,8 +785,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
break;
}
case DHCP_DECLINE:
- log_dhcp_server(server, "DECLINE (0x%x)",
- be32toh(req->message->xid));
+ log_dhcp_server(server, "DECLINE (0x%x): %s", be32toh(req->message->xid), strna(error_message));
/* TODO: make sure we don't offer this address again */
@@ -958,10 +963,10 @@ static int server_receive_message(sd_event_source *s, int fd,
if (ioctl(fd, FIONREAD, &buflen) < 0)
return -errno;
- if (buflen < 0)
+ else if (buflen < 0)
return -EIO;
- message = malloc0(buflen);
+ message = malloc(buflen);
if (!message)
return -ENOMEM;
@@ -969,9 +974,12 @@ static int server_receive_message(sd_event_source *s, int fd,
iov.iov_len = buflen;
len = recvmsg(fd, &msg, 0);
- if (len < buflen)
- return 0;
- else if ((size_t)len < sizeof(DHCPMessage))
+ if (len < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
+
+ return -errno;
+ } else if ((size_t)len < sizeof(DHCPMessage))
return 0;
CMSG_FOREACH(cmsg, &msg) {
diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c
index acb31a16c2..0e7327b895 100644
--- a/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libsystemd-network/sd-dhcp6-client.c
@@ -24,17 +24,19 @@
#include <sys/ioctl.h>
#include <linux/if_infiniband.h>
-#include "udev.h"
-#include "udev-util.h"
-#include "util.h"
-#include "random-util.h"
-
-#include "network-internal.h"
#include "sd-dhcp6-client.h"
-#include "dhcp6-protocol.h"
+
+#include "alloc-util.h"
+#include "dhcp-identifier.h"
#include "dhcp6-internal.h"
#include "dhcp6-lease-internal.h"
-#include "dhcp-identifier.h"
+#include "dhcp6-protocol.h"
+#include "fd-util.h"
+#include "in-addr-util.h"
+#include "network-internal.h"
+#include "random-util.h"
+#include "string-table.h"
+#include "util.h"
#define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
@@ -45,6 +47,7 @@ struct sd_dhcp6_client {
sd_event *event;
int event_priority;
int index;
+ struct in6_addr local_address;
uint8_t mac_addr[MAX_MAC_ADDR_LEN];
size_t mac_addr_len;
uint16_t arp_type;
@@ -69,10 +72,10 @@ struct sd_dhcp6_client {
};
static const uint16_t default_req_opts[] = {
- DHCP6_OPTION_DNS_SERVERS,
- DHCP6_OPTION_DOMAIN_LIST,
- DHCP6_OPTION_NTP_SERVER,
- DHCP6_OPTION_SNTP_SERVERS,
+ SD_DHCP6_OPTION_DNS_SERVERS,
+ SD_DHCP6_OPTION_DOMAIN_LIST,
+ SD_DHCP6_OPTION_NTP_SERVER,
+ SD_DHCP6_OPTION_SNTP_SERVERS,
};
const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
@@ -104,11 +107,8 @@ const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
-#define _cleanup_dhcp6_client_unref_ _cleanup_(sd_dhcp6_client_unrefp)
-
#define DHCP6_CLIENT_DONT_DESTROY(client) \
- _cleanup_dhcp6_client_unref_ _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
+ _cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
@@ -132,6 +132,18 @@ int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index) {
return 0;
}
+int sd_dhcp6_client_set_local_address(sd_dhcp6_client *client, const struct in6_addr *local_address) {
+ assert_return(client, -EINVAL);
+ assert_return(local_address, -EINVAL);
+ assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) local_address) > 0, -EINVAL);
+
+ assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
+
+ client->local_address = *local_address;
+
+ return 0;
+}
+
int sd_dhcp6_client_set_mac(
sd_dhcp6_client *client,
const uint8_t *addr, size_t addr_len,
@@ -208,9 +220,8 @@ int sd_dhcp6_client_set_duid(
return 0;
}
-int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, bool enabled) {
+int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, int enabled) {
assert_return(client, -EINVAL);
-
assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
client->information_request = enabled;
@@ -218,7 +229,7 @@ int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, bool enable
return 0;
}
-int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client, bool *enabled) {
+int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client, int *enabled) {
assert_return(client, -EINVAL);
assert_return(enabled, -EINVAL);
@@ -234,10 +245,10 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option)
assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
switch(option) {
- case DHCP6_OPTION_DNS_SERVERS:
- case DHCP6_OPTION_DOMAIN_LIST:
- case DHCP6_OPTION_SNTP_SERVERS:
- case DHCP6_OPTION_NTP_SERVER:
+ case SD_DHCP6_OPTION_DNS_SERVERS:
+ case SD_DHCP6_OPTION_DOMAIN_LIST:
+ case SD_DHCP6_OPTION_SNTP_SERVERS:
+ case SD_DHCP6_OPTION_NTP_SERVER:
break;
default:
@@ -259,12 +270,12 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option)
int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
assert_return(client, -EINVAL);
- assert_return(ret, -EINVAL);
if (!client->lease)
return -ENOMSG;
- *ret = client->lease;
+ if (ret)
+ *ret = client->lease;
return 0;
}
@@ -351,7 +362,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
message->type = DHCP6_SOLICIT;
r = dhcp6_option_append(&opt, &optlen,
- DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
+ SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
if (r < 0)
return r;
@@ -369,7 +380,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
else
message->type = DHCP6_RENEW;
- r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID,
+ r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_SERVERID,
client->lease->serverid_len,
client->lease->serverid);
if (r < 0)
@@ -395,14 +406,14 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
return -EINVAL;
}
- r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ORO,
+ r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ORO,
client->req_opts_len * sizeof(be16_t),
client->req_opts);
if (r < 0)
return r;
assert (client->duid_len);
- r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
+ r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_CLIENTID,
client->duid_len, &client->duid);
if (r < 0)
return r;
@@ -413,7 +424,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
else
elapsed_time = 0xffff;
- r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ELAPSED_TIME,
+ r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ELAPSED_TIME,
sizeof(elapsed_time), &elapsed_time);
if (r < 0)
return r;
@@ -595,8 +606,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec,
}
log_dhcp6_client(client, "Next retransmission in %s",
- format_timespan(time_string, FORMAT_TIMESPAN_MAX,
- client->retransmit_time, 0));
+ format_timespan(time_string, FORMAT_TIMESPAN_MAX, client->retransmit_time, USEC_PER_SEC));
r = sd_event_add_time(client->event, &client->timeout_resend,
clock_boottime_or_monotonic(),
@@ -677,7 +687,7 @@ static int client_parse_message(sd_dhcp6_client *client,
while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
&optval)) >= 0) {
switch (optcode) {
- case DHCP6_OPTION_CLIENTID:
+ case SD_DHCP6_OPTION_CLIENTID:
if (clientid) {
log_dhcp6_client(client, "%s contains multiple clientids",
dhcp6_message_type_to_string(message->type));
@@ -695,7 +705,7 @@ static int client_parse_message(sd_dhcp6_client *client,
break;
- case DHCP6_OPTION_SERVERID:
+ case SD_DHCP6_OPTION_SERVERID:
r = dhcp6_lease_get_serverid(lease, &id, &id_len);
if (r >= 0 && id) {
log_dhcp6_client(client, "%s contains multiple serverids",
@@ -709,7 +719,7 @@ static int client_parse_message(sd_dhcp6_client *client,
break;
- case DHCP6_OPTION_PREFERENCE:
+ case SD_DHCP6_OPTION_PREFERENCE:
if (optlen != 1)
return -EINVAL;
@@ -719,7 +729,7 @@ static int client_parse_message(sd_dhcp6_client *client,
break;
- case DHCP6_OPTION_STATUS_CODE:
+ case SD_DHCP6_OPTION_STATUS_CODE:
if (optlen < 2)
return -EINVAL;
@@ -733,7 +743,7 @@ static int client_parse_message(sd_dhcp6_client *client,
break;
- case DHCP6_OPTION_IA_NA:
+ case SD_DHCP6_OPTION_IA_NA:
if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
log_dhcp6_client(client, "Information request ignoring IA NA option");
@@ -757,35 +767,35 @@ static int client_parse_message(sd_dhcp6_client *client,
break;
- case DHCP6_OPTION_RAPID_COMMIT:
+ case SD_DHCP6_OPTION_RAPID_COMMIT:
r = dhcp6_lease_set_rapid_commit(lease);
if (r < 0)
return r;
break;
- case DHCP6_OPTION_DNS_SERVERS:
+ case SD_DHCP6_OPTION_DNS_SERVERS:
r = dhcp6_lease_set_dns(lease, optval, optlen);
if (r < 0)
return r;
break;
- case DHCP6_OPTION_DOMAIN_LIST:
+ case SD_DHCP6_OPTION_DOMAIN_LIST:
r = dhcp6_lease_set_domains(lease, optval, optlen);
if (r < 0)
return r;
break;
- case DHCP6_OPTION_NTP_SERVER:
+ case SD_DHCP6_OPTION_NTP_SERVER:
r = dhcp6_lease_set_ntp(lease, optval, optlen);
if (r < 0)
return r;
break;
- case DHCP6_OPTION_SNTP_SERVERS:
+ case SD_DHCP6_OPTION_SNTP_SERVERS:
r = dhcp6_lease_set_sntp(lease, optval, optlen);
if (r < 0)
return r;
@@ -816,7 +826,7 @@ static int client_parse_message(sd_dhcp6_client *client,
static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, size_t len) {
int r;
- _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
+ _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
bool rapid_commit;
if (reply->type != DHCP6_REPLY)
@@ -847,7 +857,7 @@ static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, si
static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *advertise, size_t len) {
int r;
- _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
+ _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
uint8_t pref_advertise = 0, pref_lease = 0;
if (advertise->type != DHCP6_ADVERTISE)
@@ -882,7 +892,7 @@ static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *adver
static int client_receive_message(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
sd_dhcp6_client *client = userdata;
DHCP6_CLIENT_DONT_DESTROY(client);
- _cleanup_free_ DHCP6Message *message;
+ _cleanup_free_ DHCP6Message *message = NULL;
int r, buflen, len;
assert(s);
@@ -890,18 +900,26 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
assert(client->event);
r = ioctl(fd, FIONREAD, &buflen);
- if (r < 0 || buflen <= 0)
- buflen = DHCP6_MIN_OPTIONS_SIZE;
+ if (r < 0)
+ return -errno;
+ else if (buflen < 0)
+ /* This really should not happen */
+ return -EIO;
- message = malloc0(buflen);
+ message = malloc(buflen);
if (!message)
return -ENOMEM;
len = read(fd, message, buflen);
- if ((size_t)len < sizeof(DHCP6Message)) {
- log_dhcp6_client(client, "could not receive message from UDP socket: %m");
+ if (len < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
+
+ log_dhcp6_client(client, "Could not receive message from UDP socket: %m");
+
+ return -errno;
+ } else if ((size_t)len < sizeof(DHCP6Message))
return 0;
- }
switch(message->type) {
case DHCP6_SOLICIT:
@@ -1048,9 +1066,7 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
log_dhcp6_client(client, "T1 expires in %s",
- format_timespan(time_string,
- FORMAT_TIMESPAN_MAX,
- timeout, 0));
+ format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
r = sd_event_add_time(client->event,
&client->lease->ia.timeout_t1,
@@ -1072,9 +1088,7 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
log_dhcp6_client(client, "T2 expires in %s",
- format_timespan(time_string,
- FORMAT_TIMESPAN_MAX,
- timeout, 0));
+ format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
r = sd_event_add_time(client->event,
&client->lease->ia.timeout_t2,
@@ -1120,11 +1134,19 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
}
int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
+ assert_return(client, -EINVAL);
+
client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP);
return 0;
}
+int sd_dhcp6_client_is_running(sd_dhcp6_client *client) {
+ assert_return(client, -EINVAL);
+
+ return client->state != DHCP6_STATE_STOPPED;
+}
+
int sd_dhcp6_client_start(sd_dhcp6_client *client) {
int r = 0;
enum DHCP6State state = DHCP6_STATE_SOLICITATION;
@@ -1132,9 +1154,10 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) {
assert_return(client, -EINVAL);
assert_return(client->event, -EINVAL);
assert_return(client->index > 0, -EINVAL);
+ assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) &client->local_address) > 0, -EINVAL);
if (!IN_SET(client->state, DHCP6_STATE_STOPPED))
- return -EALREADY;
+ return -EBUSY;
r = client_reset(client);
if (r < 0)
@@ -1148,7 +1171,7 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) {
if (r < 0)
return r;
- r = dhcp6_network_bind_udp_socket(client->index, NULL);
+ r = dhcp6_network_bind_udp_socket(client->index, &client->local_address);
if (r < 0)
return r;
@@ -1251,7 +1274,7 @@ 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;
+ _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
size_t t;
assert_return(ret, -EINVAL);
@@ -1262,7 +1285,7 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
client->n_ref = 1;
- client->ia_na.type = DHCP6_OPTION_IA_NA;
+ client->ia_na.type = SD_DHCP6_OPTION_IA_NA;
client->index = -1;
diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c
index f34af6eaba..327759e180 100644
--- a/src/libsystemd-network/sd-dhcp6-lease.c
+++ b/src/libsystemd-network/sd-dhcp6-lease.c
@@ -22,11 +22,11 @@
#include <errno.h>
-#include "strv.h"
-#include "util.h"
-
+#include "alloc-util.h"
#include "dhcp6-lease-internal.h"
#include "dhcp6-protocol.h"
+#include "strv.h"
+#include "util.h"
int dhcp6_lease_clear_timers(DHCP6IA *ia) {
assert_return(ia, -EINVAL);
@@ -256,7 +256,7 @@ int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
assert_return(lease, -EINVAL);
assert_return(optval, -EINVAL);
- free(lease->ntp);
+ lease->ntp = mfree(lease->ntp);
lease->ntp_count = 0;
lease->ntp_allocated = 0;
diff --git a/src/libsystemd-network/sd-icmp6-nd.c b/src/libsystemd-network/sd-icmp6-nd.c
deleted file mode 100644
index bedcac8d9e..0000000000
--- a/src/libsystemd-network/sd-icmp6-nd.c
+++ /dev/null
@@ -1,722 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 Intel Corporation. All rights reserved.
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General 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 <netinet/icmp6.h>
-#include <netinet/ip6.h>
-#include <string.h>
-#include <stdbool.h>
-#include <netinet/in.h>
-#include <sys/ioctl.h>
-
-#include "socket-util.h"
-#include "async.h"
-
-#include "dhcp6-internal.h"
-#include "sd-icmp6-nd.h"
-
-#define ICMP6_ROUTER_SOLICITATION_INTERVAL 4 * USEC_PER_SEC
-#define ICMP6_MAX_ROUTER_SOLICITATIONS 3
-
-enum icmp6_nd_state {
- ICMP6_NEIGHBOR_DISCOVERY_IDLE = 0,
- ICMP6_ROUTER_SOLICITATION_SENT = 10,
- ICMP6_ROUTER_ADVERTISMENT_LISTEN = 11,
-};
-
-#define IP6_MIN_MTU (unsigned)1280
-#define ICMP6_ND_RECV_SIZE (IP6_MIN_MTU - sizeof(struct ip6_hdr))
-#define ICMP6_OPT_LEN_UNITS 8
-
-typedef struct ICMP6Prefix ICMP6Prefix;
-
-struct ICMP6Prefix {
- unsigned n_ref;
-
- LIST_FIELDS(ICMP6Prefix, prefixes);
-
- uint8_t len;
- sd_event_source *timeout_valid;
- struct in6_addr addr;
-};
-
-struct sd_icmp6_nd {
- unsigned n_ref;
-
- enum icmp6_nd_state state;
- sd_event *event;
- int event_priority;
- int index;
- struct ether_addr mac_addr;
- uint32_t mtu;
- ICMP6Prefix *expired_prefix;
- LIST_HEAD(ICMP6Prefix, prefixes);
- int fd;
- sd_event_source *recv;
- sd_event_source *timeout;
- int nd_sent;
- sd_icmp6_nd_callback_t callback;
- void *userdata;
-};
-
-#define log_icmp6_nd(p, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "ICMPv6 CLIENT: " fmt, ##__VA_ARGS__)
-
-static ICMP6Prefix *icmp6_prefix_unref(ICMP6Prefix *prefix) {
-
- if (!prefix)
- return NULL;
-
- assert(prefix->n_ref > 0);
- prefix->n_ref--;
-
- if (prefix->n_ref > 0)
- return NULL;
-
- prefix->timeout_valid = sd_event_source_unref(prefix->timeout_valid);
- free(prefix);
- return NULL;
-}
-
-static int icmp6_prefix_new(ICMP6Prefix **ret) {
- _cleanup_free_ ICMP6Prefix *prefix = NULL;
-
- assert(ret);
-
- prefix = new0(ICMP6Prefix, 1);
- if (!prefix)
- return -ENOMEM;
-
- prefix->n_ref = 1;
- LIST_INIT(prefixes, prefix);
-
- *ret = prefix;
- prefix = NULL;
-
- return 0;
-}
-
-static void icmp6_nd_notify(sd_icmp6_nd *nd, int event) {
- if (nd->callback)
- nd->callback(nd, event, nd->userdata);
-}
-
-int sd_icmp6_nd_set_callback(sd_icmp6_nd *nd, sd_icmp6_nd_callback_t callback,
- void *userdata) {
- assert(nd);
-
- nd->callback = callback;
- nd->userdata = userdata;
-
- return 0;
-}
-
-int sd_icmp6_nd_set_index(sd_icmp6_nd *nd, int interface_index) {
- assert(nd);
- assert(interface_index >= -1);
-
- nd->index = interface_index;
-
- return 0;
-}
-
-int sd_icmp6_nd_set_mac(sd_icmp6_nd *nd, const struct ether_addr *mac_addr) {
- assert(nd);
-
- if (mac_addr)
- memcpy(&nd->mac_addr, mac_addr, sizeof(nd->mac_addr));
- else
- zero(nd->mac_addr);
-
- return 0;
-
-}
-
-int sd_icmp6_nd_attach_event(sd_icmp6_nd *nd, sd_event *event, int priority) {
- int r;
-
- assert_return(nd, -EINVAL);
- assert_return(!nd->event, -EBUSY);
-
- if (event)
- nd->event = sd_event_ref(event);
- else {
- r = sd_event_default(&nd->event);
- if (r < 0)
- return 0;
- }
-
- nd->event_priority = priority;
-
- return 0;
-}
-
-int sd_icmp6_nd_detach_event(sd_icmp6_nd *nd) {
- assert_return(nd, -EINVAL);
-
- nd->event = sd_event_unref(nd->event);
-
- return 0;
-}
-
-sd_event *sd_icmp6_nd_get_event(sd_icmp6_nd *nd) {
- assert(nd);
-
- return nd->event;
-}
-
-sd_icmp6_nd *sd_icmp6_nd_ref(sd_icmp6_nd *nd) {
-
- if (!nd)
- return NULL;
-
- assert(nd->n_ref > 0);
- nd->n_ref++;
-
- return nd;
-}
-
-static int icmp6_nd_init(sd_icmp6_nd *nd) {
- assert(nd);
-
- nd->recv = sd_event_source_unref(nd->recv);
- nd->fd = asynchronous_close(nd->fd);
- nd->timeout = sd_event_source_unref(nd->timeout);
-
- return 0;
-}
-
-sd_icmp6_nd *sd_icmp6_nd_unref(sd_icmp6_nd *nd) {
- ICMP6Prefix *prefix, *p;
-
- if (!nd)
- return NULL;
-
- assert(nd->n_ref > 0);
- nd->n_ref--;
-
- if (nd->n_ref > 0)
- return NULL;
-
- icmp6_nd_init(nd);
- sd_icmp6_nd_detach_event(nd);
-
- LIST_FOREACH_SAFE(prefixes, prefix, p, nd->prefixes) {
- LIST_REMOVE(prefixes, nd->prefixes, prefix);
-
- prefix = icmp6_prefix_unref(prefix);
- }
-
- free(nd);
-
- return NULL;
-}
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_icmp6_nd*, sd_icmp6_nd_unref);
-#define _cleanup_sd_icmp6_nd_free_ _cleanup_(sd_icmp6_nd_unrefp)
-
-int sd_icmp6_nd_new(sd_icmp6_nd **ret) {
- _cleanup_sd_icmp6_nd_free_ sd_icmp6_nd *nd = NULL;
-
- assert(ret);
-
- nd = new0(sd_icmp6_nd, 1);
- if (!nd)
- return -ENOMEM;
-
- nd->n_ref = 1;
-
- nd->index = -1;
- nd->fd = -1;
-
- LIST_HEAD_INIT(nd->prefixes);
-
- *ret = nd;
- nd = NULL;
-
- return 0;
-}
-
-int sd_icmp6_ra_get_mtu(sd_icmp6_nd *nd, uint32_t *mtu) {
- assert_return(nd, -EINVAL);
- assert_return(mtu, -EINVAL);
-
- if (nd->mtu == 0)
- return -ENOMSG;
-
- *mtu = nd->mtu;
-
- return 0;
-}
-
-static int icmp6_ra_prefix_timeout(sd_event_source *s, uint64_t usec,
- void *userdata) {
- sd_icmp6_nd *nd = userdata;
- ICMP6Prefix *prefix, *p;
-
- assert(nd);
-
- LIST_FOREACH_SAFE(prefixes, prefix, p, nd->prefixes) {
- if (prefix->timeout_valid != s)
- continue;
-
- log_icmp6_nd(nd, "Prefix expired "SD_ICMP6_ND_ADDRESS_FORMAT_STR"/%d",
- SD_ICMP6_ND_ADDRESS_FORMAT_VAL(prefix->addr),
- prefix->len);
-
- LIST_REMOVE(prefixes, nd->prefixes, prefix);
-
- nd->expired_prefix = prefix;
- icmp6_nd_notify(nd,
- SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_PREFIX_EXPIRED);
- nd->expired_prefix = NULL;
-
- prefix = icmp6_prefix_unref(prefix);
-
- break;
- }
-
- return 0;
-}
-
-static int icmp6_ra_prefix_set_timeout(sd_icmp6_nd *nd,
- ICMP6Prefix *prefix,
- usec_t valid) {
- usec_t time_now;
- int r;
-
- assert_return(prefix, -EINVAL);
-
- r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now);
- if (r < 0)
- return r;
-
- prefix->timeout_valid = sd_event_source_unref(prefix->timeout_valid);
-
- r = sd_event_add_time(nd->event, &prefix->timeout_valid,
- clock_boottime_or_monotonic(), time_now + valid,
- USEC_PER_SEC, icmp6_ra_prefix_timeout, nd);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_priority(prefix->timeout_valid,
- nd->event_priority);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_description(prefix->timeout_valid,
- "icmp6-prefix-timeout");
-
-error:
- if (r < 0)
- prefix->timeout_valid =
- sd_event_source_unref(prefix->timeout_valid);
-
- return r;
-}
-
-static int icmp6_prefix_match(const struct in6_addr *prefix, uint8_t prefixlen,
- const struct in6_addr *addr,
- uint8_t addr_prefixlen) {
- uint8_t bytes, mask, len;
-
- assert_return(prefix, -EINVAL);
- assert_return(addr, -EINVAL);
-
- len = MIN(prefixlen, addr_prefixlen);
-
- bytes = len / 8;
- mask = 0xff << (8 - len % 8);
-
- if (memcmp(prefix, addr, bytes) != 0 ||
- (prefix->s6_addr[bytes] & mask) != (addr->s6_addr[bytes] & mask))
- return -EADDRNOTAVAIL;
-
- return 0;
-}
-
-static int icmp6_ra_prefix_match(ICMP6Prefix *head, const struct in6_addr *addr,
- uint8_t addr_len, ICMP6Prefix **result) {
- ICMP6Prefix *prefix;
-
- LIST_FOREACH(prefixes, prefix, head) {
- if (icmp6_prefix_match(&prefix->addr, prefix->len, addr,
- addr_len) >= 0) {
- *result = prefix;
- return 0;
- }
- }
-
- return -EADDRNOTAVAIL;
-}
-
-int sd_icmp6_prefix_match(struct in6_addr *prefix, uint8_t prefixlen,
- struct in6_addr *addr) {
- return icmp6_prefix_match(prefix, prefixlen, addr,
- sizeof(addr->s6_addr) * 8);
-}
-
-int sd_icmp6_ra_get_prefixlen(sd_icmp6_nd *nd, const struct in6_addr *addr,
- uint8_t *prefixlen) {
- int r;
- ICMP6Prefix *prefix;
-
- assert_return(nd, -EINVAL);
- assert_return(addr, -EINVAL);
- assert_return(prefixlen, -EINVAL);
-
- r = icmp6_ra_prefix_match(nd->prefixes, addr,
- sizeof(addr->s6_addr) * 8, &prefix);
- if (r < 0)
- return r;
-
- *prefixlen = prefix->len;
-
- return 0;
-}
-
-int sd_icmp6_ra_get_expired_prefix(sd_icmp6_nd *nd, struct in6_addr **addr, uint8_t *prefixlen) {
- assert_return(nd, -EINVAL);
- assert_return(addr, -EINVAL);
- assert_return(prefixlen, -EINVAL);
-
- if (!nd->expired_prefix)
- return -EADDRNOTAVAIL;
-
- *addr = &nd->expired_prefix->addr;
- *prefixlen = nd->expired_prefix->len;
-
- return 0;
-}
-
-static int icmp6_ra_prefix_update(sd_icmp6_nd *nd, ssize_t len,
- const struct nd_opt_prefix_info *prefix_opt) {
- int r;
- ICMP6Prefix *prefix;
- uint32_t lifetime;
- char time_string[FORMAT_TIMESPAN_MAX];
-
- assert_return(nd, -EINVAL);
- assert_return(prefix_opt, -EINVAL);
-
- if (len < prefix_opt->nd_opt_pi_len)
- return -ENOMSG;
-
- if (!(prefix_opt->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK))
- return 0;
-
- lifetime = be32toh(prefix_opt->nd_opt_pi_valid_time);
-
- r = icmp6_ra_prefix_match(nd->prefixes,
- &prefix_opt->nd_opt_pi_prefix,
- prefix_opt->nd_opt_pi_prefix_len, &prefix);
-
- if (r < 0 && r != -EADDRNOTAVAIL)
- return r;
-
- /* if router advertisment prefix valid timeout is zero, the timeout
- callback will be called immediately to clean up the prefix */
-
- if (r == -EADDRNOTAVAIL) {
- r = icmp6_prefix_new(&prefix);
- if (r < 0)
- return r;
-
- prefix->len = prefix_opt->nd_opt_pi_prefix_len;
-
- memcpy(&prefix->addr, &prefix_opt->nd_opt_pi_prefix,
- sizeof(prefix->addr));
-
- log_icmp6_nd(nd, "New prefix "SD_ICMP6_ND_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s",
- SD_ICMP6_ND_ADDRESS_FORMAT_VAL(prefix->addr),
- prefix->len, lifetime,
- format_timespan(time_string, FORMAT_TIMESPAN_MAX,
- lifetime * USEC_PER_SEC, 0));
-
- LIST_PREPEND(prefixes, nd->prefixes, prefix);
-
- } else {
- if (prefix->len != prefix_opt->nd_opt_pi_prefix_len) {
- uint8_t prefixlen;
-
- prefixlen = MIN(prefix->len, prefix_opt->nd_opt_pi_prefix_len);
-
- log_icmp6_nd(nd, "Prefix length mismatch %d/%d using %d",
- prefix->len,
- prefix_opt->nd_opt_pi_prefix_len,
- prefixlen);
-
- prefix->len = prefixlen;
- }
-
- log_icmp6_nd(nd, "Update prefix "SD_ICMP6_ND_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s",
- SD_ICMP6_ND_ADDRESS_FORMAT_VAL(prefix->addr),
- prefix->len, lifetime,
- format_timespan(time_string, FORMAT_TIMESPAN_MAX,
- lifetime * USEC_PER_SEC, 0));
- }
-
- r = icmp6_ra_prefix_set_timeout(nd, prefix, lifetime * USEC_PER_SEC);
-
- return r;
-}
-
-static int icmp6_ra_parse(sd_icmp6_nd *nd, struct nd_router_advert *ra,
- ssize_t len) {
- void *opt;
- struct nd_opt_hdr *opt_hdr;
-
- assert_return(nd, -EINVAL);
- assert_return(ra, -EINVAL);
-
- len -= sizeof(*ra);
- if (len < ICMP6_OPT_LEN_UNITS) {
- log_icmp6_nd(nd, "Router Advertisement below minimum length");
-
- return -ENOMSG;
- }
-
- opt = ra + 1;
- opt_hdr = opt;
-
- while (len != 0 && len >= opt_hdr->nd_opt_len * ICMP6_OPT_LEN_UNITS) {
- struct nd_opt_mtu *opt_mtu;
- uint32_t mtu;
- struct nd_opt_prefix_info *opt_prefix;
-
- if (opt_hdr->nd_opt_len == 0)
- return -ENOMSG;
-
- switch (opt_hdr->nd_opt_type) {
- case ND_OPT_MTU:
- opt_mtu = opt;
-
- mtu = be32toh(opt_mtu->nd_opt_mtu_mtu);
-
- if (mtu != nd->mtu) {
- nd->mtu = MAX(mtu, IP6_MIN_MTU);
-
- log_icmp6_nd(nd, "Router Advertisement link MTU %d using %d",
- mtu, nd->mtu);
- }
-
- break;
-
- case ND_OPT_PREFIX_INFORMATION:
- opt_prefix = opt;
-
- icmp6_ra_prefix_update(nd, len, opt_prefix);
-
- break;
- }
-
- len -= opt_hdr->nd_opt_len * ICMP6_OPT_LEN_UNITS;
- opt = (void *)((char *)opt +
- opt_hdr->nd_opt_len * ICMP6_OPT_LEN_UNITS);
- opt_hdr = opt;
- }
-
- if (len > 0)
- log_icmp6_nd(nd, "Router Advertisement contains %zd bytes of trailing garbage", len);
-
- return 0;
-}
-
-static int icmp6_router_advertisment_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- sd_icmp6_nd *nd = userdata;
- int r, buflen = 0;
- ssize_t len;
- _cleanup_free_ struct nd_router_advert *ra = NULL;
- int event = SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_NONE;
-
- assert(s);
- assert(nd);
- assert(nd->event);
-
- r = ioctl(fd, FIONREAD, &buflen);
- if (r < 0 || buflen <= 0)
- buflen = ICMP6_ND_RECV_SIZE;
-
- ra = malloc(buflen);
- if (!ra)
- return -ENOMEM;
-
- len = read(fd, ra, buflen);
- if (len < 0) {
- log_icmp6_nd(nd, "Could not receive message from UDP socket: %m");
- return 0;
- }
-
- if (ra->nd_ra_type != ND_ROUTER_ADVERT)
- return 0;
-
- if (ra->nd_ra_code != 0)
- return 0;
-
- nd->timeout = sd_event_source_unref(nd->timeout);
-
- nd->state = ICMP6_ROUTER_ADVERTISMENT_LISTEN;
-
- if (ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER )
- event = SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_OTHER;
-
- if (ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED)
- event = SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_MANAGED;
-
- log_icmp6_nd(nd, "Received Router Advertisement flags %s/%s",
- ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED? "MANAGED": "none",
- ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER? "OTHER": "none");
-
- if (event != SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_NONE) {
- r = icmp6_ra_parse(nd, ra, len);
- if (r < 0) {
- log_icmp6_nd(nd, "Could not parse Router Advertisement: %s",
- strerror(-r));
- return 0;
- }
- }
-
- icmp6_nd_notify(nd, event);
-
- return 0;
-}
-
-static int icmp6_router_solicitation_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
- sd_icmp6_nd *nd = userdata;
- uint64_t time_now, next_timeout;
- struct ether_addr unset = { };
- struct ether_addr *addr = NULL;
- int r;
-
- assert(s);
- assert(nd);
- assert(nd->event);
-
- nd->timeout = sd_event_source_unref(nd->timeout);
-
- if (nd->nd_sent >= ICMP6_MAX_ROUTER_SOLICITATIONS) {
- icmp6_nd_notify(nd, SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_TIMEOUT);
- nd->state = ICMP6_ROUTER_ADVERTISMENT_LISTEN;
- } else {
- if (memcmp(&nd->mac_addr, &unset, sizeof(struct ether_addr)))
- addr = &nd->mac_addr;
-
- r = dhcp_network_icmp6_send_router_solicitation(nd->fd, addr);
- if (r < 0)
- log_icmp6_nd(nd, "Error sending Router Solicitation");
- else {
- nd->state = ICMP6_ROUTER_SOLICITATION_SENT;
- log_icmp6_nd(nd, "Sent Router Solicitation");
- }
-
- nd->nd_sent++;
-
- r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now);
- if (r < 0) {
- icmp6_nd_notify(nd, r);
- return 0;
- }
-
- next_timeout = time_now + ICMP6_ROUTER_SOLICITATION_INTERVAL;
-
- r = sd_event_add_time(nd->event, &nd->timeout, clock_boottime_or_monotonic(),
- next_timeout, 0,
- icmp6_router_solicitation_timeout, nd);
- if (r < 0) {
- icmp6_nd_notify(nd, r);
- return 0;
- }
-
- r = sd_event_source_set_priority(nd->timeout,
- nd->event_priority);
- if (r < 0) {
- icmp6_nd_notify(nd, r);
- return 0;
- }
-
- r = sd_event_source_set_description(nd->timeout, "icmp6-timeout");
- if (r < 0) {
- icmp6_nd_notify(nd, r);
- return 0;
- }
- }
-
- return 0;
-}
-
-int sd_icmp6_nd_stop(sd_icmp6_nd *nd) {
- assert_return(nd, -EINVAL);
- assert_return(nd->event, -EINVAL);
-
- log_icmp6_nd(client, "Stop ICMPv6");
-
- icmp6_nd_init(nd);
-
- nd->state = ICMP6_NEIGHBOR_DISCOVERY_IDLE;
-
- return 0;
-}
-
-int sd_icmp6_router_solicitation_start(sd_icmp6_nd *nd) {
- int r;
-
- assert(nd);
- assert(nd->event);
-
- if (nd->state != ICMP6_NEIGHBOR_DISCOVERY_IDLE)
- return -EINVAL;
-
- if (nd->index < 1)
- return -EINVAL;
-
- r = dhcp_network_icmp6_bind_router_solicitation(nd->index);
- if (r < 0)
- return r;
-
- nd->fd = r;
-
- r = sd_event_add_io(nd->event, &nd->recv, nd->fd, EPOLLIN,
- icmp6_router_advertisment_recv, nd);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_priority(nd->recv, nd->event_priority);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_description(nd->recv, "icmp6-receive-message");
- if (r < 0)
- goto error;
-
- r = sd_event_add_time(nd->event, &nd->timeout, clock_boottime_or_monotonic(),
- 0, 0, icmp6_router_solicitation_timeout, nd);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_priority(nd->timeout, nd->event_priority);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_description(nd->timeout, "icmp6-timeout");
-error:
- if (r < 0)
- icmp6_nd_init(nd);
- else
- log_icmp6_nd(client, "Start Router Solicitation");
-
- return r;
-}
diff --git a/src/libsystemd-network/sd-ipv4acd.c b/src/libsystemd-network/sd-ipv4acd.c
index 95b96bfd52..f7880a891c 100644
--- a/src/libsystemd-network/sd-ipv4acd.c
+++ b/src/libsystemd-network/sd-ipv4acd.c
@@ -24,17 +24,18 @@
#include <stdlib.h>
#include <string.h>
-#include "event-util.h"
+#include "sd-ipv4acd.h"
+
+#include "alloc-util.h"
+#include "arp-util.h"
+#include "fd-util.h"
#include "in-addr-util.h"
#include "list.h"
-#include "refcnt.h"
#include "random-util.h"
+#include "refcnt.h"
#include "siphash24.h"
#include "util.h"
-#include "arp-util.h"
-#include "sd-ipv4acd.h"
-
/* Constants from the RFC */
#define PROBE_WAIT 1
#define PROBE_NUM 3
@@ -118,11 +119,8 @@ sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *ll) {
return NULL;
}
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4acd*, sd_ipv4acd_unref);
-#define _cleanup_ipv4acd_unref_ _cleanup_(sd_ipv4acd_unrefp)
-
int sd_ipv4acd_new(sd_ipv4acd **ret) {
- _cleanup_ipv4acd_unref_ sd_ipv4acd *ll = NULL;
+ _cleanup_(sd_ipv4acd_unrefp) sd_ipv4acd *ll = NULL;
assert_return(ret, -EINVAL);
@@ -187,7 +185,7 @@ int sd_ipv4acd_stop(sd_ipv4acd *ll) {
static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata);
static int ipv4acd_set_next_wakeup(sd_ipv4acd *ll, int sec, int random_sec) {
- _cleanup_event_source_unref_ sd_event_source *timer = NULL;
+ _cleanup_(sd_event_source_unrefp) sd_event_source *timer = NULL;
usec_t next_timeout;
usec_t time_now;
int r;
@@ -468,7 +466,7 @@ int sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address){
return 0;
}
-bool sd_ipv4acd_is_running(sd_ipv4acd *ll) {
+int sd_ipv4acd_is_running(sd_ipv4acd *ll) {
assert_return(ll, false);
return ll->state != IPV4ACD_STATE_INIT;
diff --git a/src/libsystemd-network/sd-ipv4ll.c b/src/libsystemd-network/sd-ipv4ll.c
index dd427ddd78..db6cf22aaa 100644
--- a/src/libsystemd-network/sd-ipv4ll.c
+++ b/src/libsystemd-network/sd-ipv4ll.c
@@ -18,13 +18,17 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
+#include <arpa/inet.h>
#include <errno.h>
-#include <string.h>
#include <stdio.h>
-#include <arpa/inet.h>
+#include <stdlib.h>
+#include <string.h>
-#include "event-util.h"
+#include "sd-ipv4acd.h"
+#include "sd-ipv4ll.h"
+
+#include "alloc-util.h"
+#include "in-addr-util.h"
#include "list.h"
#include "random-util.h"
#include "refcnt.h"
@@ -32,14 +36,11 @@
#include "sparse-endian.h"
#include "util.h"
-#include "sd-ipv4acd.h"
-#include "sd-ipv4ll.h"
-
#define IPV4LL_NETWORK 0xA9FE0000L
#define IPV4LL_NETMASK 0xFFFF0000L
#define IPV4LL_DONT_DESTROY(ll) \
- _cleanup_ipv4ll_unref_ _unused_ sd_ipv4ll *_dont_destroy_##ll = sd_ipv4ll_ref(ll)
+ _cleanup_(sd_ipv4ll_unrefp) _unused_ sd_ipv4ll *_dont_destroy_##ll = sd_ipv4ll_ref(ll)
struct sd_ipv4ll {
unsigned n_ref;
@@ -84,13 +85,10 @@ sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) {
return NULL;
}
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4ll*, sd_ipv4ll_unref);
-#define _cleanup_ipv4ll_unref_ _cleanup_(sd_ipv4ll_unrefp)
-
static void ipv4ll_on_acd(sd_ipv4acd *ll, int event, void *userdata);
int sd_ipv4ll_new(sd_ipv4ll **ret) {
- _cleanup_ipv4ll_unref_ sd_ipv4ll *ll = NULL;
+ _cleanup_(sd_ipv4ll_unrefp) sd_ipv4ll *ll = NULL;
int r;
assert_return(ret, -EINVAL);
@@ -99,6 +97,8 @@ int sd_ipv4ll_new(sd_ipv4ll **ret) {
if (!ll)
return -ENOMEM;
+ ll->n_ref = 1;
+
r = sd_ipv4acd_new(&ll->acd);
if (r < 0)
return r;
@@ -107,8 +107,6 @@ int sd_ipv4ll_new(sd_ipv4ll **ret) {
if (r < 0)
return r;
- ll->n_ref = 1;
-
*ret = ll;
ll = NULL;
@@ -141,15 +139,14 @@ int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) {
assert_return(ll, -EINVAL);
if (!ll->random_data) {
- uint8_t seed[8];
+ uint64_t seed;
/* If no random data is set, generate some from the MAC */
- siphash24(seed, &addr->ether_addr_octet,
- ETH_ALEN, HASH_KEY.bytes);
+ seed = siphash24(&addr->ether_addr_octet, ETH_ALEN, HASH_KEY.bytes);
assert_cc(sizeof(unsigned) <= 8);
- r = sd_ipv4ll_set_address_seed(ll, *(unsigned*)seed);
+ r = sd_ipv4ll_set_address_seed(ll, (unsigned) htole64(seed));
if (r < 0)
return r;
}
@@ -226,12 +223,45 @@ int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, unsigned seed) {
return 0;
}
-bool sd_ipv4ll_is_running(sd_ipv4ll *ll) {
+int sd_ipv4ll_is_running(sd_ipv4ll *ll) {
assert_return(ll, false);
return sd_ipv4acd_is_running(ll->acd);
}
+static bool ipv4ll_address_is_valid(const struct in_addr *address) {
+ uint32_t addr;
+
+ assert(address);
+
+ if (!in_addr_is_link_local(AF_INET, (const union in_addr_union *) address))
+ return false;
+
+ addr = be32toh(address->s_addr);
+
+ if ((addr & 0x0000FF00) == 0x0000 ||
+ (addr & 0x0000FF00) == 0xFF00)
+ return false;
+
+ return true;
+}
+
+int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address) {
+ int r;
+
+ assert_return(ll, -EINVAL);
+ assert_return(address, -EINVAL);
+ assert_return(ipv4ll_address_is_valid(address), -EINVAL);
+
+ r = sd_ipv4acd_set_address(ll->acd, address);
+ if (r < 0)
+ return r;
+
+ ll->address = address->s_addr;
+
+ return 0;
+}
+
static int ipv4ll_pick_address(sd_ipv4ll *ll) {
struct in_addr in_addr;
be32_t addr;
@@ -247,18 +277,15 @@ static int ipv4ll_pick_address(sd_ipv4ll *ll) {
return r;
addr = htonl((random & 0x0000FFFF) | IPV4LL_NETWORK);
} while (addr == ll->address ||
- (ntohl(addr) & IPV4LL_NETMASK) != IPV4LL_NETWORK ||
(ntohl(addr) & 0x0000FF00) == 0x0000 ||
(ntohl(addr) & 0x0000FF00) == 0xFF00);
in_addr.s_addr = addr;
- r = sd_ipv4acd_set_address(ll->acd, &in_addr);
+ r = sd_ipv4ll_set_address(ll, &in_addr);
if (r < 0)
return r;
- ll->address = addr;
-
return 0;
}
diff --git a/src/libsystemd-network/sd-lldp.c b/src/libsystemd-network/sd-lldp.c
index 17512884f5..1c696f9ef0 100644
--- a/src/libsystemd-network/sd-lldp.c
+++ b/src/libsystemd-network/sd-lldp.c
@@ -22,15 +22,18 @@
#include <arpa/inet.h>
-#include "siphash24.h"
-#include "hashmap.h"
+#include "sd-lldp.h"
-#include "lldp-tlv.h"
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "hashmap.h"
+#include "lldp-internal.h"
#include "lldp-port.h"
-#include "sd-lldp.h"
+#include "lldp-tlv.h"
#include "prioq.h"
-#include "lldp-internal.h"
-#include "lldp-util.h"
+#include "siphash24.h"
+#include "string-util.h"
typedef enum LLDPAgentRXState {
LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL = 4,
@@ -68,16 +71,14 @@ struct sd_lldp {
lldp_agent_statistics statistics;
};
-static unsigned long chassis_id_hash_func(const void *p,
- const uint8_t hash_key[HASH_KEY_SIZE]) {
- uint64_t u;
+static void chassis_id_hash_func(const void *p, struct siphash *state) {
const lldp_chassis_id *id = p;
assert(id);
+ assert(id->data);
- siphash24((uint8_t *) &u, id->data, id->length, hash_key);
-
- return (unsigned long) u;
+ siphash24_compress(&id->length, sizeof(id->length), state);
+ siphash24_compress(id->data, id->length, state);
}
static int chassis_id_compare_func(const void *_a, const void *_b) {
@@ -144,12 +145,9 @@ static int lldp_receive_frame(sd_lldp *lldp, tlv_packet *tlv) {
/* 10.3.2 LLDPDU validation: rxProcessFrame() */
int lldp_handle_packet(tlv_packet *tlv, uint16_t length) {
+ bool system_description = false, system_name = false, chassis_id = false;
+ bool malformed = false, port_id = false, ttl = false, end = false;
uint16_t type, len, i, l, t;
- bool chassis_id = false;
- bool malformed = false;
- bool port_id = false;
- bool ttl = false;
- bool end = false;
lldp_port *port;
uint8_t *p, *q;
sd_lldp *lldp;
@@ -162,8 +160,7 @@ int lldp_handle_packet(tlv_packet *tlv, uint16_t length) {
lldp = (sd_lldp *) port->userdata;
if (lldp->port->status == LLDP_PORT_STATUS_DISABLED) {
- log_lldp("Port is disabled : %s . Dropping ...",
- lldp->port->ifname);
+ log_lldp("Port: %s is disabled. Dropping.", lldp->port->ifname);
goto out;
}
@@ -181,8 +178,7 @@ int lldp_handle_packet(tlv_packet *tlv, uint16_t length) {
if (type == LLDP_TYPE_END) {
if (len != 0) {
- log_lldp("TLV type end is not length 0. Length:%d received . Dropping ...",
- len);
+ log_lldp("TLV type end must be length 0 (not %d). Dropping.", len);
malformed = true;
goto out;
@@ -192,14 +188,13 @@ int lldp_handle_packet(tlv_packet *tlv, uint16_t length) {
break;
} else if (type >=_LLDP_TYPE_MAX) {
- log_lldp("TLV type not recognized %d . Dropping ...",
- type);
+ log_lldp("TLV type: %d not recognized. Dropping.", type);
malformed = true;
goto out;
}
- /* skip type and lengh encoding */
+ /* skip type and length encoding */
p += 2;
q = p;
@@ -208,7 +203,7 @@ int lldp_handle_packet(tlv_packet *tlv, uint16_t length) {
if (i <= 3) {
if (i != type) {
- log_lldp("TLV missing or out of order. Dropping ...");
+ log_lldp("TLV missing or out of order. Dropping.");
malformed = true;
goto out;
@@ -219,25 +214,22 @@ int lldp_handle_packet(tlv_packet *tlv, uint16_t length) {
case LLDP_TYPE_CHASSIS_ID:
if (len < 2) {
- log_lldp("Received malformed Chassis ID TLV len = %d. Dropping",
- len);
+ log_lldp("Received malformed Chassis ID TLV length: %d. Dropping.", len);
malformed = true;
goto out;
}
if (chassis_id) {
- log_lldp("Duplicate Chassis ID TLV found. Dropping ...");
+ log_lldp("Duplicate Chassis ID TLV found. Dropping.");
malformed = true;
goto out;
}
/* Look what subtype it has */
- if (*q == LLDP_CHASSIS_SUBTYPE_RESERVED ||
- *q > LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED) {
- log_lldp("Unknown subtype: %d found in Chassis ID TLV . Dropping ...",
- *q);
+ if (*q == LLDP_CHASSIS_SUBTYPE_RESERVED || *q > LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED) {
+ log_lldp("Unknown subtype: %d found in Chassis ID TLV. Dropping.", *q);
malformed = true;
goto out;
@@ -250,25 +242,22 @@ int lldp_handle_packet(tlv_packet *tlv, uint16_t length) {
case LLDP_TYPE_PORT_ID:
if (len < 2) {
- log_lldp("Received malformed Port ID TLV len = %d. Dropping",
- len);
+ log_lldp("Received malformed Port ID TLV length: %d. Dropping.", len);
malformed = true;
goto out;
}
if (port_id) {
- log_lldp("Duplicate Port ID TLV found. Dropping ...");
+ log_lldp("Duplicate Port ID TLV found. Dropping.");
malformed = true;
goto out;
}
/* Look what subtype it has */
- if (*q == LLDP_PORT_SUBTYPE_RESERVED ||
- *q > LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED) {
- log_lldp("Unknown subtype: %d found in Port ID TLV . Dropping ...",
- *q);
+ if (*q == LLDP_PORT_SUBTYPE_RESERVED || *q > LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED) {
+ log_lldp("Unknown subtype: %d found in Port ID TLV. Dropping.", *q);
malformed = true;
goto out;
@@ -281,16 +270,14 @@ int lldp_handle_packet(tlv_packet *tlv, uint16_t length) {
case LLDP_TYPE_TTL:
if(len != 2) {
- log_lldp(
- "Received invalid lenth: %d TTL TLV. Dropping ...",
- len);
+ log_lldp("Received invalid TTL TLV lenth: %d. Dropping.", len);
malformed = true;
goto out;
}
if (ttl) {
- log_lldp("Duplicate TTL TLV found. Dropping ...");
+ log_lldp("Duplicate TTL TLV found. Dropping.");
malformed = true;
goto out;
@@ -299,11 +286,45 @@ int lldp_handle_packet(tlv_packet *tlv, uint16_t length) {
ttl = true;
break;
+ case LLDP_TYPE_SYSTEM_NAME:
+
+ /* According to RFC 1035 the length of a FQDN is limited to 255 characters */
+ if (len > 255) {
+ log_lldp("Received invalid system name length: %d. Dropping.", len);
+ malformed = true;
+ goto out;
+ }
+
+ if (system_name) {
+ log_lldp("Duplicate system name found. Dropping.");
+ malformed = true;
+ goto out;
+ }
+
+ system_name = true;
+
+ break;
+ case LLDP_TYPE_SYSTEM_DESCRIPTION:
+
+ /* 0 <= n <= 255 octets */
+ if (len > 255) {
+ log_lldp("Received invalid system description length: %d. Dropping.", len);
+ malformed = true;
+ goto out;
+ }
+
+ if (system_description) {
+ log_lldp("Duplicate system description found. Dropping.");
+ malformed = true;
+ goto out;
+ }
+
+ system_description = true;
+ break;
default:
if (len == 0) {
- log_lldp("TLV type = %d's, length 0 received . Dropping ...",
- type);
+ log_lldp("TLV type: %d length 0 received. Dropping.", type);
malformed = true;
goto out;
@@ -313,7 +334,7 @@ int lldp_handle_packet(tlv_packet *tlv, uint16_t length) {
}
if(!chassis_id || !port_id || !ttl || !end) {
- log_lldp( "One or more mandotory TLV missing . Dropping ...");
+ log_lldp("One or more mandatory TLV missing. Dropping.");
malformed = true;
goto out;
@@ -322,7 +343,7 @@ int lldp_handle_packet(tlv_packet *tlv, uint16_t length) {
r = tlv_packet_parse_pdu(tlv, length);
if (r < 0) {
- log_lldp( "Failed to parse the TLV. Dropping ...");
+ log_lldp("Failed to parse the TLV. Dropping.");
malformed = true;
goto out;
@@ -338,7 +359,7 @@ int lldp_handle_packet(tlv_packet *tlv, uint16_t length) {
lldp->statistics.stats_frames_in_errors_total ++;
}
- tlv_packet_free(tlv);
+ sd_lldp_packet_unref(tlv);
return 0;
}
@@ -455,7 +476,7 @@ int sd_lldp_save(sd_lldp *lldp, const char *lldp_file) {
_cleanup_free_ char *s = NULL;
char *k, *t;
- r = lldp_read_chassis_id(p->packet, &type, &length, &mac);
+ r = sd_lldp_packet_read_chassis_id(p->packet, &type, &mac, &length);
if (r < 0)
continue;
@@ -468,7 +489,7 @@ int sd_lldp_save(sd_lldp *lldp, const char *lldp_file) {
goto fail;
}
- r = lldp_read_port_id(p->packet, &type, &length, &port_id);
+ r = sd_lldp_packet_read_port_id(p->packet, &type, &port_id, &length);
if (r < 0)
continue;
@@ -513,7 +534,7 @@ int sd_lldp_save(sd_lldp *lldp, const char *lldp_file) {
free(s);
s = k;
- r = lldp_read_system_name(p->packet, &length, &k);
+ r = sd_lldp_packet_read_system_name(p->packet, &k, &length);
if (r < 0)
k = strappend(s, "'_NAME=N/A' ");
else {
@@ -535,7 +556,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) sd_lldp_packet_read_system_capability(p->packet, &data);
sprintf(buf, "'_CAP=%x'", data);
@@ -650,10 +671,10 @@ int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_cb_t cb, void *userdata) {
return 0;
}
-void sd_lldp_free(sd_lldp *lldp) {
+sd_lldp* sd_lldp_unref(sd_lldp *lldp) {
if (!lldp)
- return;
+ return NULL;
/* Drop all packets */
lldp_mib_objects_flush(lldp);
@@ -664,13 +685,14 @@ void sd_lldp_free(sd_lldp *lldp) {
prioq_free(lldp->by_expiry);
free(lldp);
+ return NULL;
}
int sd_lldp_new(int ifindex,
const char *ifname,
const struct ether_addr *mac,
sd_lldp **ret) {
- _cleanup_lldp_free_ sd_lldp *lldp = NULL;
+ _cleanup_(sd_lldp_unrefp) sd_lldp *lldp = NULL;
int r;
assert_return(ret, -EINVAL);
@@ -702,3 +724,35 @@ int sd_lldp_new(int ifindex,
return 0;
}
+
+int sd_lldp_get_packets(sd_lldp *lldp, sd_lldp_packet ***tlvs) {
+ lldp_neighbour_port *p;
+ lldp_chassis *c;
+ Iterator iter;
+ unsigned count = 0, i;
+
+ assert_return(lldp, -EINVAL);
+ assert_return(tlvs, -EINVAL);
+
+ HASHMAP_FOREACH(c, lldp->neighbour_mib, iter) {
+ LIST_FOREACH(port, p, c->ports)
+ count++;
+ }
+
+ if (!count) {
+ *tlvs = NULL;
+ return 0;
+ }
+
+ *tlvs = new(sd_lldp_packet *, count);
+ if (!*tlvs)
+ return -ENOMEM;
+
+ i = 0;
+ HASHMAP_FOREACH(c, lldp->neighbour_mib, iter) {
+ LIST_FOREACH(port, p, c->ports)
+ (*tlvs)[i++] = sd_lldp_packet_ref(p->packet);
+ }
+
+ return count;
+}
diff --git a/src/libsystemd-network/sd-ndisc.c b/src/libsystemd-network/sd-ndisc.c
new file mode 100644
index 0000000000..0ee466b32a
--- /dev/null
+++ b/src/libsystemd-network/sd-ndisc.c
@@ -0,0 +1,718 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Intel Corporation. All rights reserved.
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General 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 <netinet/icmp6.h>
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/ioctl.h>
+
+#include "sd-ndisc.h"
+
+#include "alloc-util.h"
+#include "async.h"
+#include "icmp6-util.h"
+#include "in-addr-util.h"
+#include "list.h"
+#include "socket-util.h"
+#include "string-util.h"
+
+#define NDISC_ROUTER_SOLICITATION_INTERVAL 4 * USEC_PER_SEC
+#define NDISC_MAX_ROUTER_SOLICITATIONS 3
+
+enum NDiscState {
+ NDISC_STATE_IDLE,
+ NDISC_STATE_SOLICITATION_SENT,
+ NDISC_STATE_ADVERTISMENT_LISTEN,
+ _NDISC_STATE_MAX,
+ _NDISC_STATE_INVALID = -1,
+};
+
+#define IP6_MIN_MTU (unsigned)1280
+#define ICMP6_RECV_SIZE (IP6_MIN_MTU - sizeof(struct ip6_hdr))
+#define NDISC_OPT_LEN_UNITS 8
+
+#define ND_RA_FLAG_PREF 0x18
+#define ND_RA_FLAG_PREF_LOW 0x03
+#define ND_RA_FLAG_PREF_MEDIUM 0x0
+#define ND_RA_FLAG_PREF_HIGH 0x1
+#define ND_RA_FLAG_PREF_INVALID 0x2
+
+typedef struct NDiscPrefix NDiscPrefix;
+
+struct NDiscPrefix {
+ unsigned n_ref;
+
+ sd_ndisc *nd;
+
+ LIST_FIELDS(NDiscPrefix, prefixes);
+
+ uint8_t len;
+ usec_t valid_until;
+ struct in6_addr addr;
+};
+
+struct sd_ndisc {
+ unsigned n_ref;
+
+ enum NDiscState state;
+ sd_event *event;
+ int event_priority;
+ int index;
+ struct ether_addr mac_addr;
+ uint32_t mtu;
+ LIST_HEAD(NDiscPrefix, prefixes);
+ int fd;
+ sd_event_source *recv;
+ sd_event_source *timeout;
+ int nd_sent;
+ sd_ndisc_router_callback_t router_callback;
+ sd_ndisc_prefix_autonomous_callback_t prefix_autonomous_callback;
+ sd_ndisc_prefix_onlink_callback_t prefix_onlink_callback;
+ sd_ndisc_callback_t callback;
+ void *userdata;
+};
+
+#define log_ndisc(p, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "NDisc CLIENT: " fmt, ##__VA_ARGS__)
+
+static NDiscPrefix *ndisc_prefix_unref(NDiscPrefix *prefix) {
+
+ if (!prefix)
+ return NULL;
+
+ assert(prefix->n_ref > 0);
+ prefix->n_ref--;
+
+ if (prefix->n_ref > 0)
+ return NULL;
+
+ if (prefix->nd)
+ LIST_REMOVE(prefixes, prefix->nd->prefixes, prefix);
+
+ free(prefix);
+
+ return NULL;
+}
+
+static int ndisc_prefix_new(sd_ndisc *nd, NDiscPrefix **ret) {
+ NDiscPrefix *prefix;
+
+ assert(ret);
+
+ prefix = new0(NDiscPrefix, 1);
+ if (!prefix)
+ return -ENOMEM;
+
+ prefix->n_ref = 1;
+ LIST_INIT(prefixes, prefix);
+ prefix->nd = nd;
+
+ *ret = prefix;
+ return 0;
+}
+
+int sd_ndisc_set_callback(sd_ndisc *nd,
+ sd_ndisc_router_callback_t router_callback,
+ sd_ndisc_prefix_onlink_callback_t prefix_onlink_callback,
+ sd_ndisc_prefix_autonomous_callback_t prefix_autonomous_callback,
+ sd_ndisc_callback_t callback,
+ void *userdata) {
+ assert(nd);
+
+ nd->router_callback = router_callback;
+ nd->prefix_onlink_callback = prefix_onlink_callback;
+ nd->prefix_autonomous_callback = prefix_autonomous_callback;
+ nd->callback = callback;
+ nd->userdata = userdata;
+
+ return 0;
+}
+
+int sd_ndisc_set_index(sd_ndisc *nd, int interface_index) {
+ assert(nd);
+ assert(interface_index >= -1);
+
+ nd->index = interface_index;
+
+ return 0;
+}
+
+int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr) {
+ assert(nd);
+
+ if (mac_addr)
+ memcpy(&nd->mac_addr, mac_addr, sizeof(nd->mac_addr));
+ else
+ zero(nd->mac_addr);
+
+ return 0;
+
+}
+
+int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int priority) {
+ int r;
+
+ assert_return(nd, -EINVAL);
+ assert_return(!nd->event, -EBUSY);
+
+ if (event)
+ nd->event = sd_event_ref(event);
+ else {
+ r = sd_event_default(&nd->event);
+ if (r < 0)
+ return 0;
+ }
+
+ nd->event_priority = priority;
+
+ return 0;
+}
+
+int sd_ndisc_detach_event(sd_ndisc *nd) {
+ assert_return(nd, -EINVAL);
+
+ nd->event = sd_event_unref(nd->event);
+
+ return 0;
+}
+
+sd_event *sd_ndisc_get_event(sd_ndisc *nd) {
+ assert(nd);
+
+ return nd->event;
+}
+
+sd_ndisc *sd_ndisc_ref(sd_ndisc *nd) {
+
+ if (!nd)
+ return NULL;
+
+ assert(nd->n_ref > 0);
+ nd->n_ref++;
+
+ return nd;
+}
+
+static int ndisc_init(sd_ndisc *nd) {
+ assert(nd);
+
+ nd->recv = sd_event_source_unref(nd->recv);
+ nd->fd = asynchronous_close(nd->fd);
+ nd->timeout = sd_event_source_unref(nd->timeout);
+
+ return 0;
+}
+
+sd_ndisc *sd_ndisc_unref(sd_ndisc *nd) {
+ NDiscPrefix *prefix, *p;
+
+ if (!nd)
+ return NULL;
+
+ assert(nd->n_ref > 0);
+ nd->n_ref--;
+
+ if (nd->n_ref > 0)
+ return NULL;
+
+ ndisc_init(nd);
+ sd_ndisc_detach_event(nd);
+
+ LIST_FOREACH_SAFE(prefixes, prefix, p, nd->prefixes)
+ prefix = ndisc_prefix_unref(prefix);
+
+ free(nd);
+
+ return NULL;
+}
+
+int sd_ndisc_new(sd_ndisc **ret) {
+ _cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL;
+
+ assert(ret);
+
+ nd = new0(sd_ndisc, 1);
+ if (!nd)
+ return -ENOMEM;
+
+ nd->n_ref = 1;
+
+ nd->index = -1;
+ nd->fd = -1;
+
+ LIST_HEAD_INIT(nd->prefixes);
+
+ *ret = nd;
+ nd = NULL;
+
+ return 0;
+}
+
+int sd_ndisc_get_mtu(sd_ndisc *nd, uint32_t *mtu) {
+ assert_return(nd, -EINVAL);
+ assert_return(mtu, -EINVAL);
+
+ if (nd->mtu == 0)
+ return -ENOMSG;
+
+ *mtu = nd->mtu;
+
+ return 0;
+}
+
+static int prefix_match(const struct in6_addr *prefix, uint8_t prefixlen,
+ const struct in6_addr *addr,
+ uint8_t addr_prefixlen) {
+ uint8_t bytes, mask, len;
+
+ assert_return(prefix, -EINVAL);
+ assert_return(addr, -EINVAL);
+
+ len = MIN(prefixlen, addr_prefixlen);
+
+ bytes = len / 8;
+ mask = 0xff << (8 - len % 8);
+
+ if (memcmp(prefix, addr, bytes) != 0 ||
+ (prefix->s6_addr[bytes] & mask) != (addr->s6_addr[bytes] & mask))
+ return -EADDRNOTAVAIL;
+
+ return 0;
+}
+
+static int ndisc_prefix_match(sd_ndisc *nd, const struct in6_addr *addr,
+ uint8_t addr_len, NDiscPrefix **result) {
+ NDiscPrefix *prefix, *p;
+ usec_t time_now;
+ int r;
+
+ assert(nd);
+
+ r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now);
+ if (r < 0)
+ return r;
+
+ LIST_FOREACH_SAFE(prefixes, prefix, p, nd->prefixes) {
+ if (prefix->valid_until < time_now) {
+ prefix = ndisc_prefix_unref(prefix);
+ continue;
+ }
+
+ if (prefix_match(&prefix->addr, prefix->len, addr, addr_len) >= 0) {
+ *result = prefix;
+ return 0;
+ }
+ }
+
+ return -EADDRNOTAVAIL;
+}
+
+static int ndisc_prefix_update(sd_ndisc *nd, ssize_t len,
+ const struct nd_opt_prefix_info *prefix_opt) {
+ NDiscPrefix *prefix;
+ uint32_t lifetime_valid, lifetime_preferred;
+ usec_t time_now;
+ char time_string[FORMAT_TIMESPAN_MAX];
+ int r;
+
+ assert(nd);
+ assert(prefix_opt);
+
+ if (len < prefix_opt->nd_opt_pi_len)
+ return -ENOMSG;
+
+ if (!(prefix_opt->nd_opt_pi_flags_reserved & (ND_OPT_PI_FLAG_ONLINK | ND_OPT_PI_FLAG_AUTO)))
+ return 0;
+
+ if (in_addr_is_link_local(AF_INET6, (const union in_addr_union *) &prefix_opt->nd_opt_pi_prefix) > 0)
+ return 0;
+
+ lifetime_valid = be32toh(prefix_opt->nd_opt_pi_valid_time);
+ lifetime_preferred = be32toh(prefix_opt->nd_opt_pi_preferred_time);
+
+ if (lifetime_valid < lifetime_preferred)
+ return 0;
+
+ r = ndisc_prefix_match(nd, &prefix_opt->nd_opt_pi_prefix,
+ prefix_opt->nd_opt_pi_prefix_len, &prefix);
+ if (r < 0) {
+ if (r != -EADDRNOTAVAIL)
+ return r;
+
+ /* if router advertisment prefix valid timeout is zero, the timeout
+ callback will be called immediately to clean up the prefix */
+
+ r = ndisc_prefix_new(nd, &prefix);
+ if (r < 0)
+ return r;
+
+ prefix->len = prefix_opt->nd_opt_pi_prefix_len;
+
+ memcpy(&prefix->addr, &prefix_opt->nd_opt_pi_prefix,
+ sizeof(prefix->addr));
+
+ log_ndisc(nd, "New prefix "SD_NDISC_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s",
+ SD_NDISC_ADDRESS_FORMAT_VAL(prefix->addr),
+ prefix->len, lifetime_valid,
+ format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_valid * USEC_PER_SEC, USEC_PER_SEC));
+
+ LIST_PREPEND(prefixes, nd->prefixes, prefix);
+
+ } else {
+ if (prefix->len != prefix_opt->nd_opt_pi_prefix_len) {
+ uint8_t prefixlen;
+
+ prefixlen = MIN(prefix->len, prefix_opt->nd_opt_pi_prefix_len);
+
+ log_ndisc(nd, "Prefix length mismatch %d/%d using %d",
+ prefix->len,
+ prefix_opt->nd_opt_pi_prefix_len,
+ prefixlen);
+
+ prefix->len = prefixlen;
+ }
+
+ log_ndisc(nd, "Update prefix "SD_NDISC_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s",
+ SD_NDISC_ADDRESS_FORMAT_VAL(prefix->addr),
+ prefix->len, lifetime_valid,
+ format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_valid * USEC_PER_SEC, USEC_PER_SEC));
+ }
+
+ r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now);
+ if (r < 0)
+ return r;
+
+ prefix->valid_until = time_now + lifetime_valid * USEC_PER_SEC;
+
+ if ((prefix_opt->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK) && nd->prefix_onlink_callback)
+ nd->prefix_onlink_callback(nd, &prefix->addr, prefix->len, prefix->valid_until, nd->userdata);
+
+ if ((prefix_opt->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) && nd->prefix_autonomous_callback)
+ nd->prefix_autonomous_callback(nd, &prefix->addr, prefix->len, lifetime_preferred, lifetime_valid,
+ nd->userdata);
+
+ return 0;
+}
+
+static int ndisc_ra_parse(sd_ndisc *nd, struct nd_router_advert *ra, ssize_t len) {
+ void *opt;
+ struct nd_opt_hdr *opt_hdr;
+
+ assert_return(nd, -EINVAL);
+ assert_return(ra, -EINVAL);
+
+ len -= sizeof(*ra);
+ if (len < NDISC_OPT_LEN_UNITS) {
+ log_ndisc(nd, "Router Advertisement below minimum length");
+
+ return -ENOMSG;
+ }
+
+ opt = ra + 1;
+ opt_hdr = opt;
+
+ while (len != 0 && len >= opt_hdr->nd_opt_len * NDISC_OPT_LEN_UNITS) {
+ struct nd_opt_mtu *opt_mtu;
+ uint32_t mtu;
+ struct nd_opt_prefix_info *opt_prefix;
+
+ if (opt_hdr->nd_opt_len == 0)
+ return -ENOMSG;
+
+ switch (opt_hdr->nd_opt_type) {
+ case ND_OPT_MTU:
+ opt_mtu = opt;
+
+ mtu = be32toh(opt_mtu->nd_opt_mtu_mtu);
+
+ if (mtu != nd->mtu) {
+ nd->mtu = MAX(mtu, IP6_MIN_MTU);
+
+ log_ndisc(nd, "Router Advertisement link MTU %d using %d",
+ mtu, nd->mtu);
+ }
+
+ break;
+
+ case ND_OPT_PREFIX_INFORMATION:
+ opt_prefix = opt;
+
+ ndisc_prefix_update(nd, len, opt_prefix);
+
+ break;
+ }
+
+ len -= opt_hdr->nd_opt_len * NDISC_OPT_LEN_UNITS;
+ opt = (void *)((char *)opt +
+ opt_hdr->nd_opt_len * NDISC_OPT_LEN_UNITS);
+ opt_hdr = opt;
+ }
+
+ if (len > 0)
+ log_ndisc(nd, "Router Advertisement contains %zd bytes of trailing garbage", len);
+
+ return 0;
+}
+
+static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ _cleanup_free_ struct nd_router_advert *ra = NULL;
+ sd_ndisc *nd = userdata;
+ union {
+ struct cmsghdr cmsghdr;
+ uint8_t buf[CMSG_LEN(sizeof(int))];
+ } control = {};
+ struct iovec iov = {};
+ union sockaddr_union sa = {};
+ struct msghdr msg = {
+ .msg_name = &sa.sa,
+ .msg_namelen = sizeof(sa),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
+ };
+ struct cmsghdr *cmsg;
+ struct in6_addr *gw;
+ unsigned lifetime;
+ ssize_t len;
+ int r, pref, stateful, buflen = 0;
+
+ assert(s);
+ assert(nd);
+ assert(nd->event);
+
+ r = ioctl(fd, FIONREAD, &buflen);
+ if (r < 0)
+ return -errno;
+ else if (buflen < 0)
+ /* This really should not happen */
+ return -EIO;
+
+ iov.iov_len = buflen;
+
+ ra = malloc(iov.iov_len);
+ if (!ra)
+ return -ENOMEM;
+
+ iov.iov_base = ra;
+
+ len = recvmsg(fd, &msg, 0);
+ if (len < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
+
+ log_ndisc(nd, "Could not receive message from ICMPv6 socket: %m");
+ return -errno;
+ } else if ((size_t)len < sizeof(struct nd_router_advert)) {
+ return 0;
+ } else if (msg.msg_namelen == 0)
+ gw = NULL; /* only happens when running the test-suite over a socketpair */
+ else if (msg.msg_namelen != sizeof(sa.in6)) {
+ log_ndisc(nd, "Received invalid source address size from ICMPv6 socket: %zu bytes", (size_t)msg.msg_namelen);
+ return 0;
+ } else
+ gw = &sa.in6.sin6_addr;
+
+ assert(!(msg.msg_flags & MSG_CTRUNC));
+ assert(!(msg.msg_flags & MSG_TRUNC));
+
+ CMSG_FOREACH(cmsg, &msg) {
+ if (cmsg->cmsg_level == SOL_IPV6 &&
+ cmsg->cmsg_type == IPV6_HOPLIMIT &&
+ cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
+ int hops = *(int*)CMSG_DATA(cmsg);
+
+ if (hops != 255) {
+ log_ndisc(nd, "Received RA with invalid hop limit %d. Ignoring.", hops);
+ return 0;
+ }
+
+ break;
+ }
+ }
+
+ if (gw && !in_addr_is_link_local(AF_INET6, (const union in_addr_union*) gw)) {
+ _cleanup_free_ char *addr = NULL;
+
+ (void)in_addr_to_string(AF_INET6, (const union in_addr_union*) gw, &addr);
+
+ log_ndisc(nd, "Received RA from non-link-local address %s. Ignoring.", strna(addr));
+ return 0;
+ }
+
+ if (ra->nd_ra_type != ND_ROUTER_ADVERT)
+ return 0;
+
+ if (ra->nd_ra_code != 0)
+ return 0;
+
+ nd->timeout = sd_event_source_unref(nd->timeout);
+
+ nd->state = NDISC_STATE_ADVERTISMENT_LISTEN;
+
+ stateful = ra->nd_ra_flags_reserved & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER);
+ pref = (ra->nd_ra_flags_reserved & ND_RA_FLAG_PREF) >> 3;
+
+ switch (pref) {
+ case ND_RA_FLAG_PREF_LOW:
+ case ND_RA_FLAG_PREF_HIGH:
+ break;
+ default:
+ pref = ND_RA_FLAG_PREF_MEDIUM;
+ break;
+ }
+
+ lifetime = be16toh(ra->nd_ra_router_lifetime);
+
+ log_ndisc(nd, "Received Router Advertisement: flags %s preference %s lifetime %u sec",
+ stateful & ND_RA_FLAG_MANAGED ? "MANAGED" : stateful & ND_RA_FLAG_OTHER ? "OTHER" : "none",
+ pref == ND_RA_FLAG_PREF_HIGH ? "high" : pref == ND_RA_FLAG_PREF_LOW ? "low" : "medium",
+ lifetime);
+
+ r = ndisc_ra_parse(nd, ra, len);
+ if (r < 0) {
+ log_ndisc(nd, "Could not parse Router Advertisement: %s", strerror(-r));
+ return 0;
+ }
+
+ if (nd->router_callback)
+ nd->router_callback(nd, stateful, gw, lifetime, pref, nd->userdata);
+
+ return 0;
+}
+
+static int ndisc_router_solicitation_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
+ sd_ndisc *nd = userdata;
+ uint64_t time_now, next_timeout;
+ int r;
+
+ assert(s);
+ assert(nd);
+ assert(nd->event);
+
+ nd->timeout = sd_event_source_unref(nd->timeout);
+
+ if (nd->nd_sent >= NDISC_MAX_ROUTER_SOLICITATIONS) {
+ if (nd->callback)
+ nd->callback(nd, SD_NDISC_EVENT_TIMEOUT, nd->userdata);
+ nd->state = NDISC_STATE_ADVERTISMENT_LISTEN;
+ } else {
+ r = icmp6_send_router_solicitation(nd->fd, &nd->mac_addr);
+ if (r < 0)
+ log_ndisc(nd, "Error sending Router Solicitation");
+ else {
+ nd->state = NDISC_STATE_SOLICITATION_SENT;
+ log_ndisc(nd, "Sent Router Solicitation");
+ }
+
+ nd->nd_sent++;
+
+ assert_se(sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now) >= 0);
+
+ next_timeout = time_now + NDISC_ROUTER_SOLICITATION_INTERVAL;
+
+ r = sd_event_add_time(nd->event, &nd->timeout, clock_boottime_or_monotonic(),
+ next_timeout, 0,
+ ndisc_router_solicitation_timeout, nd);
+ if (r < 0) {
+ /* we cannot continue if we are unable to rearm the timer */
+ sd_ndisc_stop(nd);
+ return 0;
+ }
+
+ r = sd_event_source_set_priority(nd->timeout, nd->event_priority);
+ if (r < 0)
+ return 0;
+
+ r = sd_event_source_set_description(nd->timeout, "ndisc-timeout");
+ if (r < 0)
+ return 0;
+ }
+
+ return 0;
+}
+
+int sd_ndisc_stop(sd_ndisc *nd) {
+ assert_return(nd, -EINVAL);
+ assert_return(nd->event, -EINVAL);
+
+ log_ndisc(client, "Stop NDisc");
+
+ ndisc_init(nd);
+
+ nd->state = NDISC_STATE_IDLE;
+
+ if (nd->callback)
+ nd->callback(nd, SD_NDISC_EVENT_STOP, nd->userdata);
+
+ return 0;
+}
+
+int sd_ndisc_router_discovery_start(sd_ndisc *nd) {
+ int r;
+
+ assert(nd);
+ assert(nd->event);
+
+ if (nd->state != NDISC_STATE_IDLE)
+ return -EBUSY;
+
+ if (nd->index < 1)
+ return -EINVAL;
+
+ r = icmp6_bind_router_solicitation(nd->index);
+ if (r < 0)
+ return r;
+
+ nd->fd = r;
+
+ r = sd_event_add_io(nd->event, &nd->recv, nd->fd, EPOLLIN,
+ ndisc_router_advertisment_recv, nd);
+ if (r < 0)
+ goto error;
+
+ r = sd_event_source_set_priority(nd->recv, nd->event_priority);
+ if (r < 0)
+ goto error;
+
+ r = sd_event_source_set_description(nd->recv, "ndisc-receive-message");
+ if (r < 0)
+ goto error;
+
+ r = sd_event_add_time(nd->event, &nd->timeout, clock_boottime_or_monotonic(),
+ 0, 0, ndisc_router_solicitation_timeout, nd);
+ if (r < 0)
+ goto error;
+
+ r = sd_event_source_set_priority(nd->timeout, nd->event_priority);
+ if (r < 0)
+ goto error;
+
+ r = sd_event_source_set_description(nd->timeout, "ndisc-timeout");
+error:
+ if (r < 0)
+ ndisc_init(nd);
+ else
+ log_ndisc(client, "Start Router Solicitation");
+
+ return r;
+}
diff --git a/src/libsystemd-network/sd-pppoe.c b/src/libsystemd-network/sd-pppoe.c
deleted file mode 100644
index 439d4eff38..0000000000
--- a/src/libsystemd-network/sd-pppoe.c
+++ /dev/null
@@ -1,810 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 Tom Gundersen
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General 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/>.
-***/
-
-/* See RFC 2516 */
-
-#include <sys/ioctl.h>
-#include <linux/ppp_defs.h>
-#include <linux/ppp-ioctl.h>
-#include <net/if.h>
-#include <netinet/in.h>
-#include <linux/if_pppox.h>
-
-#include "sd-pppoe.h"
-
-#include "event-util.h"
-
-#include "util.h"
-#include "random-util.h"
-#include "socket-util.h"
-#include "async.h"
-#include "utf8.h"
-
-#define PPPOE_MAX_PACKET_SIZE 1484
-#define PPPOE_MAX_PADR_RESEND 16
-
-/* TODO: move this to socket-util.h without getting into
- * a mess with the includes */
-union sockaddr_union_pppox {
- struct sockaddr sa;
- struct sockaddr_pppox pppox;
-};
-
-typedef enum PPPoEState {
- PPPOE_STATE_INITIALIZING,
- PPPOE_STATE_REQUESTING,
- PPPOE_STATE_RUNNING,
- PPPOE_STATE_STOPPED,
- _PPPOE_STATE_MAX,
- _PPPOE_STATE_INVALID = -1,
-} PPPoEState;
-
-typedef struct PPPoETags {
- char *service_name;
- char *ac_name;
- uint8_t *host_uniq;
- size_t host_uniq_len;
- uint8_t *cookie;
- size_t cookie_len;
-} PPPoETags;
-
-struct sd_pppoe {
- unsigned n_ref;
-
- PPPoEState state;
- uint64_t host_uniq;
-
- int ifindex;
- char *ifname;
-
- sd_event *event;
- int event_priority;
- int fd;
- sd_event_source *io;
- sd_event_source *timeout;
- int padr_resend_count;
-
- char *service_name;
- struct ether_addr peer_mac;
- be16_t session_id;
-
- int pppoe_fd;
- int channel;
-
- sd_pppoe_cb_t cb;
- void *userdata;
-
- PPPoETags tags;
-};
-
-#define PPPOE_PACKET_LENGTH(header) \
- be16toh((header)->length)
-
-#define PPPOE_PACKET_TAIL(packet) \
- (struct pppoe_tag*)((uint8_t*)(packet) + sizeof(struct pppoe_hdr) + PPPOE_PACKET_LENGTH(packet))
-
-#define PPPOE_TAG_LENGTH(tag) \
- be16toh((tag)->tag_len)
-
-#define PPPOE_TAG_TYPE(tag) \
- (tag)->tag_type
-
-#define PPPOE_TAG_NEXT(tag) \
- (struct pppoe_tag *)((uint8_t *)(tag) + sizeof(struct pppoe_tag) + PPPOE_TAG_LENGTH(tag))
-
-#define PPPOE_TAGS_FOREACH(tag, header) \
- for (tag = (header)->tag; \
- ((uint8_t *)(tag) + sizeof(struct pppoe_tag) < (uint8_t*)PPPOE_PACKET_TAIL(header)) && \
- (PPPOE_TAG_NEXT(tag) <= PPPOE_PACKET_TAIL(header)) && \
- (tag >= (header)->tag) && \
- (PPPOE_TAG_TYPE(tag) != PTT_EOL); \
- tag = PPPOE_TAG_NEXT(tag))
-
-static void pppoe_tags_clear(PPPoETags *tags) {
- free(tags->service_name);
- free(tags->ac_name);
- free(tags->host_uniq);
- free(tags->cookie);
-
- zero(*tags);
-}
-
-int sd_pppoe_set_ifindex(sd_pppoe *ppp, int ifindex) {
- assert_return(ppp, -EINVAL);
- assert_return(ifindex > 0, -EINVAL);
-
- ppp->ifindex = ifindex;
-
- return 0;
-}
-
-int sd_pppoe_set_ifname(sd_pppoe *ppp, const char *ifname) {
- char *name;
-
- assert_return(ppp, -EINVAL);
- assert_return(ifname, -EINVAL);
-
- if (strlen(ifname) > IFNAMSIZ)
- return -EINVAL;
-
- name = strdup(ifname);
- if (!name)
- return -ENOMEM;
-
- free(ppp->ifname);
- ppp->ifname = name;
-
- return 0;
-}
-
-int sd_pppoe_set_service_name(sd_pppoe *ppp, const char *service_name) {
- _cleanup_free_ char *name = NULL;
-
- assert_return(ppp, -EINVAL);
-
- if (service_name) {
- name = strdup(service_name);
- if (!name)
- return -ENOMEM;
- }
-
- free(ppp->service_name);
- ppp->service_name = name;
- name = NULL;
-
- return 0;
-}
-
-int sd_pppoe_attach_event(sd_pppoe *ppp, sd_event *event, int priority) {
- int r;
-
- assert_return(ppp, -EINVAL);
- assert_return(!ppp->event, -EBUSY);
-
- if (event)
- ppp->event = sd_event_ref(event);
- else {
- r = sd_event_default(&ppp->event);
- if (r < 0)
- return r;
- }
-
- ppp->event_priority = priority;
-
- return 0;
-}
-
-int sd_pppoe_detach_event(sd_pppoe *ppp) {
- assert_return(ppp, -EINVAL);
-
- ppp->event = sd_event_unref(ppp->event);
-
- return 0;
-}
-
-sd_pppoe *sd_pppoe_ref(sd_pppoe *ppp) {
-
- if (!ppp)
- return NULL;
-
- assert(ppp->n_ref > 0);
- ppp->n_ref++;
-
- return ppp;
-}
-
-sd_pppoe *sd_pppoe_unref(sd_pppoe *ppp) {
-
- if (!ppp)
- return NULL;
-
- assert(ppp->n_ref > 0);
- ppp->n_ref--;
-
- if (ppp->n_ref > 0)
- return NULL;
-
- pppoe_tags_clear(&ppp->tags);
- free(ppp->ifname);
- free(ppp->service_name);
- sd_pppoe_stop(ppp);
- sd_pppoe_detach_event(ppp);
-
- free(ppp);
- return NULL;
-}
-
-int sd_pppoe_new (sd_pppoe **ret) {
- sd_pppoe *ppp;
-
- assert_return(ret, -EINVAL);
-
- ppp = new0(sd_pppoe, 1);
- if (!ppp)
- return -ENOMEM;
-
- ppp->n_ref = 1;
- ppp->state = _PPPOE_STATE_INVALID;
- ppp->ifindex = -1;
- ppp->fd = -1;
- ppp->pppoe_fd = -1;
- ppp->padr_resend_count = PPPOE_MAX_PADR_RESEND;
-
- *ret = ppp;
-
- return 0;
-}
-
-int sd_pppoe_get_channel(sd_pppoe *ppp, int *channel) {
- assert_return(ppp, -EINVAL);
- assert_return(channel, -EINVAL);
- assert_return(ppp->pppoe_fd != -1, -EUNATCH);
- assert_return(ppp->state == PPPOE_STATE_RUNNING, -EUNATCH);
-
- *channel = ppp->channel;
-
- return 0;
-}
-
-int sd_pppoe_set_callback(sd_pppoe *ppp, sd_pppoe_cb_t cb, void *userdata) {
- assert_return(ppp, -EINVAL);
-
- ppp->cb = cb;
- ppp->userdata = userdata;
-
- return 0;
-}
-
-static void pppoe_tag_append(struct pppoe_hdr *packet, size_t packet_size, be16_t tag_type, const void *tag_data, uint16_t tag_len) {
- struct pppoe_tag *tag;
-
- assert(packet);
- assert(sizeof(struct pppoe_hdr) + PPPOE_PACKET_LENGTH(packet) + sizeof(struct pppoe_tag) + tag_len <= packet_size);
- assert(!(!tag_data ^ !tag_len));
-
- tag = PPPOE_PACKET_TAIL(packet);
-
- tag->tag_len = htobe16(tag_len);
- tag->tag_type = tag_type;
- if (tag_data)
- memcpy(tag->tag_data, tag_data, tag_len);
-
- packet->length = htobe16(PPPOE_PACKET_LENGTH(packet) + sizeof(struct pppoe_tag) + tag_len);
-}
-
-static int pppoe_send(sd_pppoe *ppp, uint8_t code) {
- union sockaddr_union link = {
- .ll = {
- .sll_family = AF_PACKET,
- .sll_protocol = htons(ETH_P_PPP_DISC),
- .sll_halen = ETH_ALEN,
- },
- };
- _cleanup_free_ struct pppoe_hdr *packet = NULL;
- int r;
-
- assert(ppp);
- assert(ppp->fd != -1);
- assert(IN_SET(code, PADI_CODE, PADR_CODE, PADT_CODE));
-
- link.ll.sll_ifindex = ppp->ifindex;
- if (code == PADI_CODE)
- memset(&link.ll.sll_addr, 0xff, ETH_ALEN);
- else
- memcpy(&link.ll.sll_addr, &ppp->peer_mac, ETH_ALEN);
-
- packet = malloc0(PPPOE_MAX_PACKET_SIZE);
- if (!packet)
- return -ENOMEM;
-
- packet->ver = 0x1;
- packet->type = 0x1;
- packet->code = code;
- if (code == PADT_CODE)
- packet->sid = ppp->session_id;
-
- /* Service-Name */
- pppoe_tag_append(packet, PPPOE_MAX_PACKET_SIZE, PTT_SRV_NAME,
- ppp->service_name, ppp->service_name ? strlen(ppp->service_name) : 0);
-
- /* AC-Cookie */
- if (code == PADR_CODE && ppp->tags.cookie)
- pppoe_tag_append(packet, PPPOE_MAX_PACKET_SIZE, PTT_AC_COOKIE,
- ppp->tags.cookie, ppp->tags.cookie_len);
-
- /* Host-Uniq */
- if (code != PADT_CODE) {
- ppp->host_uniq = random_u64();
-
- pppoe_tag_append(packet, PPPOE_MAX_PACKET_SIZE, PTT_HOST_UNIQ,
- &ppp->host_uniq, sizeof(ppp->host_uniq));
- }
-
- r = sendto(ppp->fd, packet, sizeof(struct pppoe_hdr) + PPPOE_PACKET_LENGTH(packet),
- 0, &link.sa, sizeof(link.ll));
- if (r < 0)
- return -errno;
-
- return 0;
-}
-
-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 = 0;
- int r;
-
- assert(ppp);
-
- r = sd_event_now(ppp->event, clock_boottime_or_monotonic(), &next_timeout);
- if (r < 0)
- return r;
-
- next_timeout += 500 * USEC_PER_MSEC;
-
- r = sd_event_add_time(ppp->event, &timeout, clock_boottime_or_monotonic(), next_timeout,
- 10 * USEC_PER_MSEC, pppoe_timeout, ppp);
- if (r < 0)
- return r;
-
- r = sd_event_source_set_priority(timeout, ppp->event_priority);
- if (r < 0)
- return r;
-
- sd_event_source_unref(ppp->timeout);
- ppp->timeout = timeout;
- timeout = NULL;
-
- return 0;
-}
-
-static int pppoe_send_initiation(sd_pppoe *ppp) {
- int r;
-
- r = pppoe_send(ppp, PADI_CODE);
- if (r < 0)
- return r;
-
- log_debug("PPPoE: sent DISCOVER (Service-Name: %s)",
- ppp->service_name ? : "");
-
- pppoe_arm_timeout(ppp);
-
- return r;
-}
-
-static int pppoe_send_request(sd_pppoe *ppp) {
- int r;
-
- r = pppoe_send(ppp, PADR_CODE);
- if (r < 0)
- return r;
-
- log_debug("PPPoE: sent REQUEST");
-
- ppp->padr_resend_count --;
-
- pppoe_arm_timeout(ppp);
-
- return 0;
-}
-
-static int pppoe_send_terminate(sd_pppoe *ppp) {
- int r;
-
- r = pppoe_send(ppp, PADT_CODE);
- if (r < 0)
- return r;
-
- log_debug("PPPoE: sent TERMINATE");
-
- return 0;
-}
-
-static int pppoe_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
- sd_pppoe *ppp = userdata;
- int r;
-
- assert(ppp);
-
- switch (ppp->state) {
- case PPPOE_STATE_INITIALIZING:
- r = pppoe_send_initiation(ppp);
- if (r < 0)
- log_warning_errno(r, "PPPoE: sending PADI failed: %m");
-
- break;
- case PPPOE_STATE_REQUESTING:
- if (ppp->padr_resend_count <= 0) {
- log_debug("PPPoE: PADR timed out, restarting PADI");
-
- r = pppoe_send_initiation(ppp);
- if (r < 0)
- log_warning_errno(r, "PPPoE: sending PADI failed: %m");
-
- ppp->padr_resend_count = PPPOE_MAX_PADR_RESEND;
- ppp->state = PPPOE_STATE_INITIALIZING;
- } else {
- r = pppoe_send_request(ppp);
- if (r < 0)
- log_warning_errno(r, "PPPoE: sending PADR failed: %m");
- }
-
- break;
- default:
- assert_not_reached("timeout in invalid state");
- }
-
- return 0;
-}
-
-static int pppoe_tag_parse_binary(struct pppoe_tag *tag, uint8_t **ret, size_t *length) {
- uint8_t *data;
-
- assert(ret);
- assert(length);
-
- data = memdup(tag->tag_data, PPPOE_TAG_LENGTH(tag));
- if (!data)
- return -ENOMEM;
-
- free(*ret);
- *ret = data;
- *length = PPPOE_TAG_LENGTH(tag);
-
- return 0;
-}
-
-static int pppoe_tag_parse_string(struct pppoe_tag *tag, char **ret) {
- char *string;
-
- assert(ret);
-
- string = strndup(tag->tag_data, PPPOE_TAG_LENGTH(tag));
- if (!string)
- return -ENOMEM;
-
- free(*ret);
- *ret = string;
-
- return 0;
-}
-
-static int pppoe_payload_parse(PPPoETags *tags, struct pppoe_hdr *header) {
- struct pppoe_tag *tag;
- int r;
-
- assert(tags);
-
- pppoe_tags_clear(tags);
-
- PPPOE_TAGS_FOREACH(tag, header) {
- switch (PPPOE_TAG_TYPE(tag)) {
- case PTT_SRV_NAME:
- r = pppoe_tag_parse_string(tag, &tags->service_name);
- if (r < 0)
- return r;
-
- break;
- case PTT_AC_NAME:
- r = pppoe_tag_parse_string(tag, &tags->ac_name);
- if (r < 0)
- return r;
-
- break;
- case PTT_HOST_UNIQ:
- r = pppoe_tag_parse_binary(tag, &tags->host_uniq, &tags->host_uniq_len);
- if (r < 0)
- return r;
-
- break;
- case PTT_AC_COOKIE:
- r = pppoe_tag_parse_binary(tag, &tags->cookie, &tags->cookie_len);
- if (r < 0)
- return r;
-
- break;
- case PTT_SRV_ERR:
- case PTT_SYS_ERR:
- case PTT_GEN_ERR:
- {
- _cleanup_free_ char *error = NULL;
-
- /* TODO: do something more sensible with the error messages */
- r = pppoe_tag_parse_string(tag, &error);
- if (r < 0)
- return r;
-
- if (strlen(error) > 0 && utf8_is_valid(error))
- log_debug("PPPoE: error - '%s'", error);
- else
- log_debug("PPPoE: error");
-
- break;
- }
- default:
- log_debug("PPPoE: ignoring unknown PPPoE tag type: 0x%.2x", PPPOE_TAG_TYPE(tag));
- }
- }
-
- return 0;
-}
-
-static int pppoe_open_pppoe_socket(sd_pppoe *ppp) {
- int s;
-
- assert(ppp);
- assert(ppp->pppoe_fd == -1);
-
- s = socket(AF_PPPOX, SOCK_STREAM, 0);
- if (s < 0)
- return -errno;
-
- ppp->pppoe_fd = s;
-
- return 0;
-}
-
-static int pppoe_connect_pppoe_socket(sd_pppoe *ppp) {
- union sockaddr_union_pppox link = {
- .pppox = {
- .sa_family = AF_PPPOX,
- .sa_protocol = PX_PROTO_OE,
- },
- };
- int r, channel;
-
- assert(ppp);
- assert(ppp->pppoe_fd != -1);
- assert(ppp->session_id);
- assert(ppp->ifname);
-
- link.pppox.sa_addr.pppoe.sid = ppp->session_id;
- memcpy(link.pppox.sa_addr.pppoe.dev, ppp->ifname, strlen(ppp->ifname));
- memcpy(link.pppox.sa_addr.pppoe.remote, &ppp->peer_mac, ETH_ALEN);
-
- r = connect(ppp->pppoe_fd, &link.sa, sizeof(link.pppox));
- if (r < 0)
- return r;
-
- r = ioctl(ppp->pppoe_fd, PPPIOCGCHAN, &channel);
- if (r < 0)
- return -errno;
-
- ppp->channel = channel;
-
- return 0;
-}
-
-static int pppoe_handle_message(sd_pppoe *ppp, struct pppoe_hdr *packet, struct ether_addr *mac) {
- int r;
-
- assert(packet);
-
- if (packet->ver != 0x1 || packet->type != 0x1)
- return 0;
-
- r = pppoe_payload_parse(&ppp->tags, packet);
- if (r < 0)
- return 0;
-
- switch (ppp->state) {
- case PPPOE_STATE_INITIALIZING:
- if (packet->code != PADO_CODE)
- return 0;
-
- if (ppp->tags.host_uniq_len != sizeof(ppp->host_uniq) ||
- memcmp(ppp->tags.host_uniq, &ppp->host_uniq, sizeof(ppp->host_uniq)) != 0)
- return 0;
-
- log_debug("PPPoE: got OFFER (Peer: "
- "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx; "
- "Service-Name: '%s'; AC-Name: '%s')",
- mac->ether_addr_octet[0],
- mac->ether_addr_octet[1],
- mac->ether_addr_octet[2],
- mac->ether_addr_octet[3],
- mac->ether_addr_octet[4],
- mac->ether_addr_octet[5],
- ppp->tags.service_name ? : "",
- ppp->tags.ac_name ? : "");
-
- memcpy(&ppp->peer_mac, mac, ETH_ALEN);
-
- r = pppoe_open_pppoe_socket(ppp);
- if (r < 0) {
- log_warning("PPPoE: could not open socket");
- return r;
- }
-
- r = pppoe_send_request(ppp);
- if (r < 0)
- return 0;
-
- ppp->state = PPPOE_STATE_REQUESTING;
-
- break;
- case PPPOE_STATE_REQUESTING:
- if (packet->code != PADS_CODE)
- return 0;
-
- if (ppp->tags.host_uniq_len != sizeof(ppp->host_uniq) ||
- memcmp(ppp->tags.host_uniq, &ppp->host_uniq,
- sizeof(ppp->host_uniq)) != 0)
- return 0;
-
- if (memcmp(&ppp->peer_mac, mac, ETH_ALEN) != 0)
- return 0;
-
- ppp->session_id = packet->sid;
-
- log_debug("PPPoE: got CONFIRMATION (Session ID: %"PRIu16")",
- be16toh(ppp->session_id));
-
- r = pppoe_connect_pppoe_socket(ppp);
- if (r < 0) {
- log_warning("PPPoE: could not connect socket");
- return r;
- }
-
- ppp->state = PPPOE_STATE_RUNNING;
-
- ppp->timeout = sd_event_source_unref(ppp->timeout);
- assert(ppp->cb);
- ppp->cb(ppp, SD_PPPOE_EVENT_RUNNING, ppp->userdata);
-
- break;
- case PPPOE_STATE_RUNNING:
- if (packet->code != PADT_CODE)
- return 0;
-
- if (memcmp(&ppp->peer_mac, mac, ETH_ALEN) != 0)
- return 0;
-
- if (ppp->session_id != packet->sid)
- return 0;
-
- log_debug("PPPoE: got TERMINATE");
-
- ppp->state = PPPOE_STATE_STOPPED;
-
- assert(ppp->cb);
- ppp->cb(ppp, SD_PPPOE_EVENT_STOPPED, ppp->userdata);
-
- break;
- case PPPOE_STATE_STOPPED:
- break;
- default:
- assert_not_reached("PPPoE: invalid state when receiving message");
- }
-
- return 0;
-}
-
-static int pppoe_receive_message(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- sd_pppoe *ppp = userdata;
- _cleanup_free_ struct pppoe_hdr *packet = NULL;
- union sockaddr_union link = {};
- socklen_t addrlen = sizeof(link);
- int buflen = 0, len, r;
-
- assert(ppp);
- assert(fd != -1);
-
- r = ioctl(fd, FIONREAD, &buflen);
- if (r < 0)
- return r;
-
- if (buflen < 0)
- /* this can't be right */
- return -EIO;
-
- packet = malloc0(buflen);
- if (!packet)
- return -ENOMEM;
-
- len = recvfrom(fd, packet, buflen, 0, &link.sa, &addrlen);
- if (len < 0) {
- log_warning_errno(r, "PPPoE: could not receive message from raw socket: %m");
- return 0;
- } else if ((size_t)len < sizeof(struct pppoe_hdr))
- return 0;
- else if ((size_t)len != sizeof(struct pppoe_hdr) + PPPOE_PACKET_LENGTH(packet))
- return 0;
-
- if (link.ll.sll_halen != ETH_ALEN)
- /* not ethernet? */
- return 0;
-
- r = pppoe_handle_message(ppp, packet, (struct ether_addr*)&link.ll.sll_addr);
- if (r < 0)
- return r;
-
- return 1;
-}
-
-int sd_pppoe_start(sd_pppoe *ppp) {
- union sockaddr_union link = {
- .ll = {
- .sll_family = AF_PACKET,
- .sll_protocol = htons(ETH_P_PPP_DISC),
- },
- };
- _cleanup_close_ int s = -1;
- _cleanup_event_source_unref_ sd_event_source *io = NULL;
- int r;
-
- assert_return(ppp, -EINVAL);
- assert_return(ppp->fd == -1, -EBUSY);
- assert_return(!ppp->io, -EBUSY);
- assert_return(ppp->ifindex > 0, -EUNATCH);
- assert_return(ppp->ifname, -EUNATCH);
- assert_return(ppp->event, -EUNATCH);
- assert_return(ppp->cb, -EUNATCH);
-
- s = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
- if (s < 0)
- return -errno;
-
- link.ll.sll_ifindex = ppp->ifindex;
-
- r = bind(s, &link.sa, sizeof(link.ll));
- if (r < 0)
- return r;
-
- r = sd_event_add_io(ppp->event, &io,
- s, EPOLLIN, pppoe_receive_message,
- ppp);
- if (r < 0)
- return r;
-
- r = sd_event_source_set_priority(io, ppp->event_priority);
- if (r < 0)
- return r;
-
- ppp->fd = s;
- s = -1;
- ppp->io = io;
- io = NULL;
-
- r = pppoe_send_initiation(ppp);
- if (r < 0)
- return r;
-
- ppp->state = PPPOE_STATE_INITIALIZING;
-
- return 0;
-}
-
-int sd_pppoe_stop(sd_pppoe *ppp) {
- assert_return(ppp, -EINVAL);
-
- if (ppp->state == PPPOE_STATE_RUNNING)
- pppoe_send_terminate(ppp);
-
- ppp->io = sd_event_source_unref(ppp->io);
- ppp->timeout = sd_event_source_unref(ppp->timeout);
- ppp->fd = asynchronous_close(ppp->fd);
- ppp->pppoe_fd = asynchronous_close(ppp->pppoe_fd);
-
- return 0;
-}
diff --git a/src/libsystemd-network/test-acd.c b/src/libsystemd-network/test-acd.c
index 94c31af3f3..43578aa269 100644
--- a/src/libsystemd-network/test-acd.c
+++ b/src/libsystemd-network/test-acd.c
@@ -19,21 +19,20 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
#include <errno.h>
+#include <stdlib.h>
#include <unistd.h>
#include <linux/veth.h>
#include <net/if.h>
#include "sd-event.h"
-#include "sd-netlink.h"
#include "sd-ipv4acd.h"
+#include "sd-netlink.h"
-#include "util.h"
-#include "event-util.h"
-#include "netlink-util.h"
#include "in-addr-util.h"
+#include "netlink-util.h"
+#include "util.h"
static void acd_handler(sd_ipv4acd *acd, int event, void *userdata) {
assert_se(acd);
@@ -76,9 +75,9 @@ static int client_run(int ifindex, const struct in_addr *pa, const struct ether_
}
static int test_acd(const char *ifname, const char *address) {
- _cleanup_event_unref_ sd_event *e = NULL;
- _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
- _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL, *reply = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL, *reply = NULL;
union in_addr_union pa;
struct ether_addr ha;
int ifindex;
diff --git a/src/libsystemd-network/test-dhcp-client.c b/src/libsystemd-network/test-dhcp-client.c
index c112ec8134..31832d2d1e 100644
--- a/src/libsystemd-network/test-dhcp-client.c
+++ b/src/libsystemd-network/test-dhcp-client.c
@@ -24,14 +24,15 @@
#include <sys/socket.h>
#include <unistd.h>
-#include "util.h"
+#include "sd-dhcp-client.h"
#include "sd-event.h"
-#include "event-util.h"
+#include "alloc-util.h"
#include "dhcp-identifier.h"
-#include "dhcp-protocol.h"
#include "dhcp-internal.h"
-#include "sd-dhcp-client.h"
+#include "dhcp-protocol.h"
+#include "fd-util.h"
+#include "util.h"
static uint8_t mac_addr[] = {'A', 'B', 'C', '1', '2', '3'};
@@ -76,26 +77,26 @@ static void test_request_basic(sd_event *e) {
assert_se(sd_dhcp_client_set_index(client, 1) == 0);
assert_se(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_SUBNET_MASK) == -EEXIST);
+ SD_DHCP_OPTION_SUBNET_MASK) == -EEXIST);
assert_se(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_ROUTER) == -EEXIST);
+ SD_DHCP_OPTION_ROUTER) == -EEXIST);
assert_se(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_HOST_NAME) == -EEXIST);
+ SD_DHCP_OPTION_HOST_NAME) == -EEXIST);
assert_se(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_DOMAIN_NAME) == -EEXIST);
+ SD_DHCP_OPTION_DOMAIN_NAME) == -EEXIST);
assert_se(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_DOMAIN_NAME_SERVER) == -EEXIST);
+ SD_DHCP_OPTION_DOMAIN_NAME_SERVER) == -EEXIST);
assert_se(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_PAD) == -EINVAL);
+ SD_DHCP_OPTION_PAD) == -EINVAL);
assert_se(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_END) == -EINVAL);
+ SD_DHCP_OPTION_END) == -EINVAL);
assert_se(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_MESSAGE_TYPE) == -EINVAL);
+ SD_DHCP_OPTION_MESSAGE_TYPE) == -EINVAL);
assert_se(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_OVERLOAD) == -EINVAL);
+ SD_DHCP_OPTION_OVERLOAD) == -EINVAL);
assert_se(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_PARAMETER_REQUEST_LIST)
+ SD_DHCP_OPTION_PARAMETER_REQUEST_LIST)
== -EINVAL);
assert_se(sd_dhcp_client_set_request_option(client, 33) == 0);
@@ -121,7 +122,7 @@ static void test_checksum(void) {
static int check_options(uint8_t code, uint8_t len, const void *option, void *userdata) {
switch(code) {
- case DHCP_OPTION_CLIENT_IDENTIFIER:
+ case SD_DHCP_OPTION_CLIENT_IDENTIFIER:
{
uint32_t iaid;
struct duid duid;
@@ -221,7 +222,7 @@ int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port, const voi
static int test_discover_message_verify(size_t size, struct DHCPMessage *dhcp) {
int res;
- res = dhcp_option_parse(dhcp, size, check_options, NULL);
+ res = dhcp_option_parse(dhcp, size, check_options, NULL, NULL);
assert_se(res == DHCP_DISCOVER);
if (verbose)
@@ -388,11 +389,11 @@ static int test_addr_acq_recv_request(size_t size, DHCPMessage *request) {
uint8_t *msg_bytes = (uint8_t *)request;
int res;
- res = dhcp_option_parse(request, size, check_options, NULL);
+ res = dhcp_option_parse(request, size, check_options, NULL, NULL);
assert_se(res == DHCP_REQUEST);
assert_se(xid == request->xid);
- assert_se(msg_bytes[size - 1] == DHCP_OPTION_END);
+ assert_se(msg_bytes[size - 1] == SD_DHCP_OPTION_END);
if (verbose)
printf(" recv DHCP Request 0x%08x\n", be32toh(xid));
@@ -418,10 +419,10 @@ static int test_addr_acq_recv_discover(size_t size, DHCPMessage *discover) {
uint8_t *msg_bytes = (uint8_t *)discover;
int res;
- res = dhcp_option_parse(discover, size, check_options, NULL);
+ res = dhcp_option_parse(discover, size, check_options, NULL, NULL);
assert_se(res == DHCP_DISCOVER);
- assert_se(msg_bytes[size - 1] == DHCP_OPTION_END);
+ assert_se(msg_bytes[size - 1] == SD_DHCP_OPTION_END);
xid = discover->xid;
@@ -489,7 +490,7 @@ static void test_addr_acq(sd_event *e) {
}
int main(int argc, char *argv[]) {
- _cleanup_event_unref_ sd_event *e;
+ _cleanup_(sd_event_unrefp) sd_event *e;
log_set_max_level(LOG_DEBUG);
log_parse_environment();
diff --git a/src/libsystemd-network/test-dhcp-option.c b/src/libsystemd-network/test-dhcp-option.c
index b1ef174849..7b80a5bd90 100644
--- a/src/libsystemd-network/test-dhcp-option.c
+++ b/src/libsystemd-network/test-dhcp-option.c
@@ -1,15 +1,15 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-#include <stdio.h>
-#include <stdbool.h>
#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
#include <string.h>
-#include "util.h"
-#include "macro.h"
-
-#include "dhcp-protocol.h"
+#include "alloc-util.h"
#include "dhcp-internal.h"
+#include "dhcp-protocol.h"
+#include "macro.h"
+#include "util.h"
struct option_desc {
uint8_t sname[64];
@@ -29,7 +29,7 @@ static bool verbose = false;
static struct option_desc option_tests[] = {
{ {}, 0, {}, 0, { 42, 5, 65, 66, 67, 68, 69 }, 7, false, },
{ {}, 0, {}, 0, { 42, 5, 65, 66, 67, 68, 69, 0, 0,
- DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 12, true, },
+ SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 12, true, },
{ {}, 0, {}, 0, { 8, 255, 70, 71, 72 }, 5, false, },
{ {}, 0, {}, 0, { 0x35, 0x01, 0x05, 0x36, 0x04, 0x01, 0x00, 0xa8,
0xc0, 0x33, 0x04, 0x00, 0x01, 0x51, 0x80, 0x01,
@@ -37,17 +37,17 @@ static struct option_desc option_tests[] = {
0xa8, 0x00, 0x01, 0x06, 0x04, 0xc0, 0xa8, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
40, true, },
- { {}, 0, {}, 0, { DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_OFFER,
+ { {}, 0, {}, 0, { SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_OFFER,
42, 3, 0, 0, 0 }, 8, true, },
{ {}, 0, {}, 0, { 42, 2, 1, 2, 44 }, 5, false, },
{ {}, 0,
- { 222, 3, 1, 2, 3, DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_NAK }, 8,
- { DHCP_OPTION_OVERLOAD, 1, DHCP_OVERLOAD_FILE }, 3, true, },
+ { 222, 3, 1, 2, 3, SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_NAK }, 8,
+ { SD_DHCP_OPTION_OVERLOAD, 1, DHCP_OVERLOAD_FILE }, 3, true, },
- { { 1, 4, 1, 2, 3, 4, DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 9,
+ { { 1, 4, 1, 2, 3, 4, SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 9,
{ 222, 3, 1, 2, 3 }, 5,
- { DHCP_OPTION_OVERLOAD, 1,
+ { SD_DHCP_OPTION_OVERLOAD, 1,
DHCP_OVERLOAD_FILE|DHCP_OVERLOAD_SNAME }, 3, true, },
};
@@ -75,9 +75,8 @@ static const char *dhcp_type(int type) {
static void test_invalid_buffer_length(void) {
DHCPMessage message;
- assert_se(dhcp_option_parse(&message, 0, NULL, NULL) == -EINVAL);
- assert_se(dhcp_option_parse(&message, sizeof(DHCPMessage) - 1, NULL, NULL)
- == -EINVAL);
+ assert_se(dhcp_option_parse(&message, 0, NULL, NULL, NULL) == -EINVAL);
+ assert_se(dhcp_option_parse(&message, sizeof(DHCPMessage) - 1, NULL, NULL, NULL) == -EINVAL);
}
static void test_message_init(void) {
@@ -101,7 +100,7 @@ static void test_message_init(void) {
assert_se(magic[2] == 83);
assert_se(magic[3] == 99);
- assert_se(dhcp_option_parse(message, len, NULL, NULL) >= 0);
+ assert_se(dhcp_option_parse(message, len, NULL, NULL, NULL) >= 0);
}
static DHCPMessage *create_message(uint8_t *options, uint16_t optlen,
@@ -130,12 +129,12 @@ static void test_ignore_opts(uint8_t *descoption, int *descpos, int *desclen) {
while (*descpos < *desclen) {
switch(descoption[*descpos]) {
- case DHCP_OPTION_PAD:
+ case SD_DHCP_OPTION_PAD:
*descpos += 1;
break;
- case DHCP_OPTION_MESSAGE_TYPE:
- case DHCP_OPTION_OVERLOAD:
+ case SD_DHCP_OPTION_MESSAGE_TYPE:
+ case SD_DHCP_OPTION_OVERLOAD:
*descpos += 3;
break;
@@ -158,10 +157,10 @@ static int test_options_cb(uint8_t code, uint8_t len, const void *option, void *
if (!desc)
return -EINVAL;
- assert_se(code != DHCP_OPTION_PAD);
- assert_se(code != DHCP_OPTION_END);
- assert_se(code != DHCP_OPTION_MESSAGE_TYPE);
- assert_se(code != DHCP_OPTION_OVERLOAD);
+ assert_se(code != SD_DHCP_OPTION_PAD);
+ assert_se(code != SD_DHCP_OPTION_END);
+ assert_se(code != SD_DHCP_OPTION_MESSAGE_TYPE);
+ assert_se(code != SD_DHCP_OPTION_OVERLOAD);
while (desc->pos >= 0 || desc->filepos >= 0 || desc->snamepos >= 0) {
@@ -264,19 +263,12 @@ static void test_options(struct option_desc *desc) {
buflen = sizeof(DHCPMessage) + optlen;
if (!desc) {
- assert_se((res = dhcp_option_parse(message, buflen,
- test_options_cb,
- NULL)) == -ENOMSG);
+ assert_se((res = dhcp_option_parse(message, buflen, test_options_cb, NULL, NULL)) == -ENOMSG);
} else if (desc->success) {
- assert_se((res = dhcp_option_parse(message, buflen,
- test_options_cb,
- desc)) >= 0);
- assert_se(desc->pos == -1 && desc->filepos == -1 &&
- desc->snamepos == -1);
+ assert_se((res = dhcp_option_parse(message, buflen, test_options_cb, desc, NULL)) >= 0);
+ assert_se(desc->pos == -1 && desc->filepos == -1 && desc->snamepos == -1);
} else
- assert_se((res = dhcp_option_parse(message, buflen,
- test_options_cb,
- desc)) < 0);
+ assert_se((res = dhcp_option_parse(message, buflen, test_options_cb, desc, NULL)) < 0);
if (verbose)
printf("DHCP type %s\n", dhcp_type(res));
@@ -306,27 +298,27 @@ static void test_option_set(void) {
result->options[2] = 'C';
result->options[3] = 'D';
- assert_se(dhcp_option_append(result, 0, &offset, 0, DHCP_OPTION_PAD,
+ assert_se(dhcp_option_append(result, 0, &offset, 0, SD_DHCP_OPTION_PAD,
0, NULL) == -ENOBUFS);
assert_se(offset == 0);
offset = 4;
- assert_se(dhcp_option_append(result, 5, &offset, 0, DHCP_OPTION_PAD,
+ assert_se(dhcp_option_append(result, 5, &offset, 0, SD_DHCP_OPTION_PAD,
0, NULL) == -ENOBUFS);
assert_se(offset == 4);
- assert_se(dhcp_option_append(result, 6, &offset, 0, DHCP_OPTION_PAD,
+ assert_se(dhcp_option_append(result, 6, &offset, 0, SD_DHCP_OPTION_PAD,
0, NULL) >= 0);
assert_se(offset == 5);
offset = pos = 4;
len = 11;
- while (pos < len && options[pos] != DHCP_OPTION_END) {
+ while (pos < len && options[pos] != SD_DHCP_OPTION_END) {
assert_se(dhcp_option_append(result, len, &offset, DHCP_OVERLOAD_SNAME,
options[pos],
options[pos + 1],
&options[pos + 2]) >= 0);
- if (options[pos] == DHCP_OPTION_PAD)
+ if (options[pos] == SD_DHCP_OPTION_PAD)
pos++;
else
pos += 2 + options[pos + 1];
@@ -344,15 +336,15 @@ static void test_option_set(void) {
if (verbose)
printf("%2d: 0x%02x(0x%02x) (options)\n", 9, result->options[9],
- DHCP_OPTION_END);
+ SD_DHCP_OPTION_END);
- assert_se(result->options[9] == DHCP_OPTION_END);
+ assert_se(result->options[9] == SD_DHCP_OPTION_END);
if (verbose)
printf("%2d: 0x%02x(0x%02x) (options)\n", 10, result->options[10],
- DHCP_OPTION_PAD);
+ SD_DHCP_OPTION_PAD);
- assert_se(result->options[10] == DHCP_OPTION_PAD);
+ assert_se(result->options[10] == SD_DHCP_OPTION_PAD);
for (i = 0; i < pos - 8; i++) {
if (verbose)
diff --git a/src/libsystemd-network/test-dhcp-server.c b/src/libsystemd-network/test-dhcp-server.c
index 7d8a1f6bd9..4ad2e42b86 100644
--- a/src/libsystemd-network/test-dhcp-server.c
+++ b/src/libsystemd-network/test-dhcp-server.c
@@ -22,14 +22,13 @@
#include <errno.h>
+#include "sd-dhcp-server.h"
#include "sd-event.h"
-#include "event-util.h"
-#include "sd-dhcp-server.h"
#include "dhcp-server-internal.h"
static void test_pool(struct in_addr *address, unsigned size, int ret) {
- _cleanup_dhcp_server_unref_ sd_dhcp_server *server = NULL;
+ _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
assert_se(sd_dhcp_server_new(&server, 1) >= 0);
@@ -37,7 +36,7 @@ static void test_pool(struct in_addr *address, unsigned size, int ret) {
}
static int test_basic(sd_event *event) {
- _cleanup_dhcp_server_unref_ sd_dhcp_server *server = NULL;
+ _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
struct in_addr address_lo = {
.s_addr = htonl(INADDR_LOOPBACK),
};
@@ -86,7 +85,7 @@ static int test_basic(sd_event *event) {
}
static void test_message_handler(void) {
- _cleanup_dhcp_server_unref_ sd_dhcp_server *server = NULL;
+ _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
struct {
DHCPMessage message;
struct {
@@ -116,10 +115,10 @@ static void test_message_handler(void) {
.message.hlen = ETHER_ADDR_LEN,
.message.xid = htobe32(0x12345678),
.message.chaddr = { 'A', 'B', 'C', 'D', 'E', 'F' },
- .option_type.code = DHCP_OPTION_MESSAGE_TYPE,
+ .option_type.code = SD_DHCP_OPTION_MESSAGE_TYPE,
.option_type.length = 1,
.option_type.type = DHCP_DISCOVER,
- .end = DHCP_OPTION_END,
+ .end = SD_DHCP_OPTION_END,
};
struct in_addr address_lo = {
.s_addr = htonl(INADDR_LOOPBACK),
@@ -135,14 +134,14 @@ static void test_message_handler(void) {
test.end = 0;
/* TODO, shouldn't this fail? */
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER);
- test.end = DHCP_OPTION_END;
+ test.end = SD_DHCP_OPTION_END;
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER);
test.option_type.code = 0;
test.option_type.length = 0;
test.option_type.type = 0;
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0);
- test.option_type.code = DHCP_OPTION_MESSAGE_TYPE;
+ test.option_type.code = SD_DHCP_OPTION_MESSAGE_TYPE;
test.option_type.length = 1;
test.option_type.type = DHCP_DISCOVER;
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER);
@@ -164,11 +163,11 @@ static void test_message_handler(void) {
test.option_type.type = DHCP_REQUEST;
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0);
- test.option_requested_ip.code = DHCP_OPTION_REQUESTED_IP_ADDRESS;
+ test.option_requested_ip.code = SD_DHCP_OPTION_REQUESTED_IP_ADDRESS;
test.option_requested_ip.length = 4;
test.option_requested_ip.address = htobe32(0x12345678);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_NAK);
- test.option_server_id.code = DHCP_OPTION_SERVER_IDENTIFIER;
+ test.option_server_id.code = SD_DHCP_OPTION_SERVER_IDENTIFIER;
test.option_server_id.length = 4;
test.option_server_id.address = htobe32(INADDR_LOOPBACK);
test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 3);
@@ -183,7 +182,7 @@ static void test_message_handler(void) {
test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 3);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_ACK);
- test.option_client_id.code = DHCP_OPTION_CLIENT_IDENTIFIER;
+ test.option_client_id.code = SD_DHCP_OPTION_CLIENT_IDENTIFIER;
test.option_client_id.length = 7;
test.option_client_id.id[0] = 0x01;
test.option_client_id.id[1] = 'A';
@@ -198,6 +197,15 @@ static void test_message_handler(void) {
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0);
}
+static uint64_t client_id_hash_helper(DHCPClientId *id, uint8_t key[HASH_KEY_SIZE]) {
+ struct siphash state;
+
+ siphash24_init(&state, key);
+ client_id_hash_func(id, &state);
+
+ return htole64(siphash24_finalize(&state));
+}
+
static void test_client_id_hash(void) {
DHCPClientId a = {
.length = 4,
@@ -213,18 +221,18 @@ static void test_client_id_hash(void) {
b.data = (uint8_t*)strdup("abcd");
assert_se(client_id_compare_func(&a, &b) == 0);
- assert_se(client_id_hash_func(&a, hash_key) == client_id_hash_func(&b, hash_key));
+ assert_se(client_id_hash_helper(&a, hash_key) == client_id_hash_helper(&b, hash_key));
a.length = 3;
assert_se(client_id_compare_func(&a, &b) != 0);
a.length = 4;
assert_se(client_id_compare_func(&a, &b) == 0);
- assert_se(client_id_hash_func(&a, hash_key) == client_id_hash_func(&b, hash_key));
+ assert_se(client_id_hash_helper(&a, hash_key) == client_id_hash_helper(&b, hash_key));
b.length = 3;
assert_se(client_id_compare_func(&a, &b) != 0);
b.length = 4;
assert_se(client_id_compare_func(&a, &b) == 0);
- assert_se(client_id_hash_func(&a, hash_key) == client_id_hash_func(&b, hash_key));
+ assert_se(client_id_hash_helper(&a, hash_key) == client_id_hash_helper(&b, hash_key));
free(b.data);
b.data = (uint8_t*)strdup("abce");
@@ -235,7 +243,7 @@ static void test_client_id_hash(void) {
}
int main(int argc, char *argv[]) {
- _cleanup_event_unref_ sd_event *e;
+ _cleanup_(sd_event_unrefp) sd_event *e;
int r;
log_set_max_level(LOG_DEBUG);
diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c
index 0c131a9897..93f585b8d4 100644
--- a/src/libsystemd-network/test-dhcp6-client.c
+++ b/src/libsystemd-network/test-dhcp6-client.c
@@ -19,23 +19,23 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <net/ethernet.h>
#include <stdbool.h>
#include <stdio.h>
-#include <sys/types.h>
#include <sys/socket.h>
+#include <sys/types.h>
#include <unistd.h>
-#include <net/ethernet.h>
-#include "socket-util.h"
-#include "macro.h"
+#include "sd-dhcp6-client.h"
#include "sd-event.h"
-#include "event-util.h"
-#include "virt.h"
-#include "sd-dhcp6-client.h"
-#include "dhcp6-protocol.h"
#include "dhcp6-internal.h"
#include "dhcp6-lease-internal.h"
+#include "dhcp6-protocol.h"
+#include "fd-util.h"
+#include "macro.h"
+#include "socket-util.h"
+#include "virt.h"
static struct ether_addr mac_addr = {
.ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
@@ -70,11 +70,11 @@ static int test_client_basic(sd_event *e) {
sizeof (mac_addr),
ARPHRD_ETHER) >= 0);
- assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_CLIENTID) == -EINVAL);
- assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DNS_SERVERS) == -EEXIST);
- assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_NTP_SERVER) == -EEXIST);
- assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_SNTP_SERVERS) == -EEXIST);
- assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DOMAIN_LIST) == -EEXIST);
+ assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_CLIENTID) == -EINVAL);
+ assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DNS_SERVERS) == -EEXIST);
+ assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_NTP_SERVER) == -EEXIST);
+ assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_SNTP_SERVERS) == -EEXIST);
+ assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DOMAIN_LIST) == -EEXIST);
assert_se(sd_dhcp6_client_set_request_option(client, 10) == -EINVAL);
assert_se(sd_dhcp6_client_set_callback(client, NULL, NULL) >= 0);
@@ -88,9 +88,9 @@ static int test_client_basic(sd_event *e) {
static int test_option(sd_event *e) {
uint8_t packet[] = {
'F', 'O', 'O',
- 0x00, DHCP6_OPTION_ORO, 0x00, 0x07,
+ 0x00, SD_DHCP6_OPTION_ORO, 0x00, 0x07,
'A', 'B', 'C', 'D', 'E', 'F', 'G',
- 0x00, DHCP6_OPTION_VENDOR_CLASS, 0x00, 0x09,
+ 0x00, SD_DHCP6_OPTION_VENDOR_CLASS, 0x00, 0x09,
'1', '2', '3', '4', '5', '6', '7', '8', '9',
'B', 'A', 'R',
};
@@ -124,7 +124,7 @@ static int test_option(sd_event *e) {
&optval) >= 0);
pos += 4 + optlen;
assert_se(buf == &packet[pos]);
- assert_se(optcode == DHCP6_OPTION_ORO);
+ assert_se(optcode == SD_DHCP6_OPTION_ORO);
assert_se(optlen == 7);
assert_se(buflen + pos == sizeof(packet));
@@ -137,7 +137,7 @@ static int test_option(sd_event *e) {
&optval) >= 0);
pos += 4 + optlen;
assert_se(buf == &packet[pos]);
- assert_se(optcode == DHCP6_OPTION_VENDOR_CLASS);
+ assert_se(optcode == SD_DHCP6_OPTION_VENDOR_CLASS);
assert_se(optlen == 9);
assert_se(buflen + pos == sizeof(packet));
@@ -205,7 +205,7 @@ static uint8_t msg_reply[173] = {
};
static int test_advertise_option(sd_event *e) {
- _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
+ _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
DHCP6Message *advertise = (DHCP6Message *)msg_advertise;
uint8_t *optval, *opt = msg_advertise + sizeof(DHCP6Message);
uint16_t optcode;
@@ -232,13 +232,13 @@ static int test_advertise_option(sd_event *e) {
&optval)) >= 0) {
switch(optcode) {
- case DHCP6_OPTION_CLIENTID:
+ case SD_DHCP6_OPTION_CLIENTID:
assert_se(optlen == 14);
opt_clientid = true;
break;
- case DHCP6_OPTION_IA_NA:
+ case SD_DHCP6_OPTION_IA_NA:
assert_se(optlen == 94);
assert_se(!memcmp(optval, &msg_advertise[26], optlen));
@@ -257,7 +257,7 @@ static int test_advertise_option(sd_event *e) {
break;
- case DHCP6_OPTION_SERVERID:
+ case SD_DHCP6_OPTION_SERVERID:
assert_se(optlen == 14);
assert_se(!memcmp(optval, &msg_advertise[179], optlen));
@@ -265,7 +265,7 @@ static int test_advertise_option(sd_event *e) {
optlen) >= 0);
break;
- case DHCP6_OPTION_PREFERENCE:
+ case SD_DHCP6_OPTION_PREFERENCE:
assert_se(optlen == 1);
assert_se(!*optval);
@@ -273,24 +273,24 @@ static int test_advertise_option(sd_event *e) {
*optval) >= 0);
break;
- case DHCP6_OPTION_ELAPSED_TIME:
+ case SD_DHCP6_OPTION_ELAPSED_TIME:
assert_se(optlen == 2);
break;
- case DHCP6_OPTION_DNS_SERVERS:
+ case SD_DHCP6_OPTION_DNS_SERVERS:
assert_se(optlen == 16);
assert_se(dhcp6_lease_set_dns(lease, optval,
optlen) >= 0);
break;
- case DHCP6_OPTION_DOMAIN_LIST:
+ case SD_DHCP6_OPTION_DOMAIN_LIST:
assert_se(optlen == 11);
assert_se(dhcp6_lease_set_domains(lease, optval,
optlen) >= 0);
break;
- case DHCP6_OPTION_SNTP_SERVERS:
+ case SD_DHCP6_OPTION_SNTP_SERVERS:
assert_se(optlen == 16);
assert_se(dhcp6_lease_set_sntp(lease, optval,
optlen) >= 0);
@@ -379,7 +379,7 @@ static void test_client_solicit_cb(sd_dhcp6_client *client, int event,
assert_se(sd_dhcp6_lease_get_ntp_addrs(lease, &addrs) == 1);
assert_se(!memcmp(addrs, &msg_advertise[159], 16));
- assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DNS_SERVERS) == -EBUSY);
+ assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DNS_SERVERS) == -EBUSY);
if (verbose)
printf(" got DHCPv6 event %d\n", event);
@@ -407,7 +407,7 @@ static int test_client_send_reply(DHCP6Message *request) {
static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
size_t len) {
- _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
+ _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
uint8_t *optval;
uint16_t optcode;
size_t optlen;
@@ -425,7 +425,7 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
while ((r = dhcp6_option_parse(&option, &len,
&optcode, &optlen, &optval)) >= 0) {
switch(optcode) {
- case DHCP6_OPTION_CLIENTID:
+ case SD_DHCP6_OPTION_CLIENTID:
assert_se(!found_clientid);
found_clientid = true;
@@ -434,7 +434,7 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
break;
- case DHCP6_OPTION_IA_NA:
+ case SD_DHCP6_OPTION_IA_NA:
assert_se(!found_iana);
found_iana = true;
@@ -453,7 +453,7 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
break;
- case DHCP6_OPTION_SERVERID:
+ case SD_DHCP6_OPTION_SERVERID:
assert_se(!found_serverid);
found_serverid = true;
@@ -462,7 +462,7 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
break;
- case DHCP6_OPTION_ELAPSED_TIME:
+ case SD_DHCP6_OPTION_ELAPSED_TIME:
assert_se(!found_elapsed_time);
found_elapsed_time = true;
@@ -521,7 +521,7 @@ static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option,
while ((r = dhcp6_option_parse(&option, &len,
&optcode, &optlen, &optval)) >= 0) {
switch(optcode) {
- case DHCP6_OPTION_CLIENTID:
+ case SD_DHCP6_OPTION_CLIENTID:
assert_se(!found_clientid);
found_clientid = true;
@@ -530,7 +530,7 @@ static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option,
break;
- case DHCP6_OPTION_IA_NA:
+ case SD_DHCP6_OPTION_IA_NA:
assert_se(!found_iana);
found_iana = true;
@@ -540,7 +540,7 @@ static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option,
break;
- case DHCP6_OPTION_ELAPSED_TIME:
+ case SD_DHCP6_OPTION_ELAPSED_TIME:
assert_se(!found_elapsed_time);
found_elapsed_time = true;
@@ -561,6 +561,7 @@ static void test_client_information_cb(sd_dhcp6_client *client, int event,
sd_event *e = userdata;
sd_dhcp6_lease *lease;
struct in6_addr *addrs;
+ struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } };
char **domains;
assert_se(e);
@@ -589,13 +590,15 @@ static void test_client_information_cb(sd_dhcp6_client *client, int event,
assert_se(sd_dhcp6_client_set_callback(client,
test_client_solicit_cb, e) >= 0);
+ assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0);
+
assert_se(sd_dhcp6_client_start(client) >= 0);
}
static int test_client_verify_information_request(DHCP6Message *information_request,
uint8_t *option, size_t len) {
- _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
+ _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
uint8_t *optval;
uint16_t optcode;
size_t optlen;
@@ -611,7 +614,7 @@ static int test_client_verify_information_request(DHCP6Message *information_requ
while ((r = dhcp6_option_parse(&option, &len,
&optcode, &optlen, &optval)) >= 0) {
switch(optcode) {
- case DHCP6_OPTION_CLIENTID:
+ case SD_DHCP6_OPTION_CLIENTID:
assert_se(!found_clientid);
found_clientid = true;
@@ -620,17 +623,17 @@ static int test_client_verify_information_request(DHCP6Message *information_requ
break;
- case DHCP6_OPTION_IA_NA:
+ case SD_DHCP6_OPTION_IA_NA:
assert_not_reached("IA TA option must not be present");
break;
- case DHCP6_OPTION_SERVERID:
+ case SD_DHCP6_OPTION_SERVERID:
assert_not_reached("Server ID option must not be present");
break;
- case DHCP6_OPTION_ELAPSED_TIME:
+ case SD_DHCP6_OPTION_ELAPSED_TIME:
assert_se(!found_elapsed_time);
found_elapsed_time = true;
@@ -700,7 +703,8 @@ int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
static int test_client_solicit(sd_event *e) {
sd_dhcp6_client *client;
usec_t time_now = now(clock_boottime_or_monotonic());
- bool val = true;
+ struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } };
+ int val = true;
if (verbose)
printf("* %s\n", __FUNCTION__);
@@ -728,6 +732,8 @@ static int test_client_solicit(sd_event *e) {
time_now + 2 * USEC_PER_SEC, 0,
test_hangcheck, NULL) >= 0);
+ assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0);
+
assert_se(sd_dhcp6_client_start(client) >= 0);
sd_event_loop(e);
@@ -742,7 +748,7 @@ static int test_client_solicit(sd_event *e) {
}
int main(int argc, char *argv[]) {
- _cleanup_event_unref_ sd_event *e;
+ _cleanup_(sd_event_unrefp) sd_event *e;
assert_se(sd_event_new(&e) >= 0);
diff --git a/src/libsystemd-network/test-icmp6-rs.c b/src/libsystemd-network/test-icmp6-rs.c
deleted file mode 100644
index 27b0ef4572..0000000000
--- a/src/libsystemd-network/test-icmp6-rs.c
+++ /dev/null
@@ -1,357 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 Intel Corporation. All rights reserved.
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General 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 <netinet/icmp6.h>
-
-#include "socket-util.h"
-
-#include "dhcp6-internal.h"
-#include "sd-icmp6-nd.h"
-
-static struct ether_addr mac_addr = {
- .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
-};
-
-static bool verbose = false;
-static sd_event_source *test_hangcheck;
-static int test_fd[2];
-
-typedef int (*send_ra_t)(uint8_t flags);
-static send_ra_t send_ra_function;
-
-static int test_rs_hangcheck(sd_event_source *s, uint64_t usec,
- void *userdata) {
- assert_se(false);
-
- return 0;
-}
-
-int dhcp_network_icmp6_bind_router_solicitation(int index) {
- assert_se(index == 42);
-
- if (socketpair(AF_UNIX, SOCK_DGRAM, 0, test_fd) < 0)
- return -errno;
-
- return test_fd[0];
-}
-
-static int send_ra_short_prefix(uint8_t flags) {
- uint8_t advertisement[] = {
- 0x86, 0x00, 0xbe, 0xd7, 0x40, 0xc0, 0x00, 0xb4,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-
- 0x03, 0x04, 0x34, 0xc0, 0x00, 0x00, 0x01, 0xf4,
- 0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x00,
- 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
- };
-
- assert_se(write(test_fd[1], advertisement, sizeof(advertisement)) ==
- sizeof(advertisement));
-
- return 0;
-}
-
-static void test_short_prefix_cb(sd_icmp6_nd *nd, int event, void *userdata) {
- sd_event *e = userdata;
- struct {
- struct in6_addr addr;
- uint8_t prefixlen;
- bool success;
- } addrs[] = {
- { { { { 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
- 52, true },
- { { { { 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0x0d, 0xad,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
- 64, false },
- { { { { 0x20, 0x01, 0x0d, 0xb8, 0x0b, 0x16, 0xd0, 0x0d,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
- 60, true },
- { { { { 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x9d, 0xab, 0xcd,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
- 64, true },
- { { { { 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xed,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } },
- 52, true },
- };
- uint8_t prefixlen;
- unsigned int i;
-
- for (i = 0; i < ELEMENTSOF(addrs); i++) {
- printf(" %s prefix %02x%02x:%02x%02x:%02x%02x:%02x%02x",
- __FUNCTION__,
- addrs[i].addr.s6_addr[0], addrs[i].addr.s6_addr[1],
- addrs[i].addr.s6_addr[2], addrs[i].addr.s6_addr[3],
- addrs[i].addr.s6_addr[4], addrs[i].addr.s6_addr[5],
- addrs[i].addr.s6_addr[6], addrs[i].addr.s6_addr[7]);
-
- if (addrs[i].success) {
- assert_se(sd_icmp6_ra_get_prefixlen(nd, &addrs[i].addr,
- &prefixlen) >= 0);
- assert_se(addrs[i].prefixlen == prefixlen);
- printf("/%d onlink\n", prefixlen);
- } else {
- assert_se(sd_icmp6_ra_get_prefixlen(nd, &addrs[i].addr,
- &prefixlen) == -EADDRNOTAVAIL);
- printf("/128 offlink\n");
- }
- }
-
- sd_event_exit(e, 0);
-}
-
-static int send_ra_prefixes(uint8_t flags) {
- uint8_t advertisement[] = {
- 0x86, 0x00, 0xbe, 0xd7, 0x40, 0xc0, 0x00, 0xb4,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x04, 0x3f, 0xc0, 0x00, 0x00, 0x01, 0xf4,
- 0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x00,
- 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x04, 0x40, 0x00, 0x00, 0x00, 0x02, 0x58,
- 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x00, 0x00,
- 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0x0d, 0xad,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x04, 0x3c, 0x80, 0x00, 0x00, 0x03, 0x84,
- 0x00, 0x00, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00,
- 0x20, 0x01, 0x0d, 0xb8, 0x0b, 0x16, 0xd0, 0x0d,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x03, 0x84,
- 0x00, 0x00, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00,
- 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x9d, 0xab, 0xcd,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
- 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
- 0x1f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
- 0x03, 0x6c, 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74,
- 0x72, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x01, 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53
- };
-
- assert_se(write(test_fd[1], advertisement, sizeof(advertisement)) ==
- sizeof(advertisement));
-
- return 0;
-}
-
-static void test_prefixes_cb(sd_icmp6_nd *nd, int event, void *userdata) {
- sd_event *e = userdata;
- struct {
- struct in6_addr addr;
- uint8_t prefixlen;
- bool success;
- } addrs[] = {
- { { { { 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
- 63, true },
- { { { { 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0x0d, 0xad,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
- 64, false },
- { { { { 0x20, 0x01, 0x0d, 0xb8, 0x0b, 0x16, 0xd0, 0x0d,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
- 60, true },
- { { { { 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x9d, 0xab, 0xcd,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
- 64, true },
- { { { { 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xed,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } },
- 63, false },
- };
- uint8_t prefixlen;
- unsigned int i;
-
- for (i = 0; i < ELEMENTSOF(addrs); i++) {
- printf(" %s prefix %02x%02x:%02x%02x:%02x%02x:%02x%02x",
- __FUNCTION__,
- addrs[i].addr.s6_addr[0], addrs[i].addr.s6_addr[1],
- addrs[i].addr.s6_addr[2], addrs[i].addr.s6_addr[3],
- addrs[i].addr.s6_addr[4], addrs[i].addr.s6_addr[5],
- addrs[i].addr.s6_addr[6], addrs[i].addr.s6_addr[7]);
-
- if (addrs[i].success) {
- assert_se(sd_icmp6_ra_get_prefixlen(nd, &addrs[i].addr,
- &prefixlen) >= 0);
- assert_se(addrs[i].prefixlen == prefixlen);
- printf("/%d onlink\n", prefixlen);
- } else {
- assert_se(sd_icmp6_ra_get_prefixlen(nd, &addrs[i].addr,
- &prefixlen) == -EADDRNOTAVAIL);
- printf("/128 offlink\n");
- }
- }
-
- send_ra_function = send_ra_short_prefix;
- assert_se(sd_icmp6_nd_set_callback(nd, test_short_prefix_cb, e) >= 0);
- assert_se(sd_icmp6_nd_stop(nd) >= 0);
- assert_se(sd_icmp6_router_solicitation_start(nd) >= 0);
-}
-
-static void test_prefixes(void) {
- sd_event *e;
- sd_icmp6_nd *nd;
-
- if (verbose)
- printf("* %s\n", __FUNCTION__);
-
- send_ra_function = send_ra_prefixes;
-
- assert_se(sd_event_new(&e) >= 0);
-
- assert_se(sd_icmp6_nd_new(&nd) >= 0);
- assert_se(nd);
-
- assert_se(sd_icmp6_nd_attach_event(nd, e, 0) >= 0);
-
- assert_se(sd_icmp6_nd_set_index(nd, 42) >= 0);
- assert_se(sd_icmp6_nd_set_mac(nd, &mac_addr) >= 0);
- assert_se(sd_icmp6_nd_set_callback(nd, test_prefixes_cb, e) >= 0);
-
- assert_se(sd_icmp6_router_solicitation_start(nd) >= 0);
-
- sd_event_loop(e);
-
- nd = sd_icmp6_nd_unref(nd);
- assert_se(!nd);
-
- close(test_fd[1]);
-
- sd_event_unref(e);
-}
-
-static int send_ra(uint8_t flags) {
- uint8_t advertisement[] = {
- 0x86, 0x00, 0xde, 0x83, 0x40, 0xc0, 0x00, 0xb4,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x01, 0xf4,
- 0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x00,
- 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
- 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
- 0x1f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
- 0x03, 0x6c, 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74,
- 0x72, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x01, 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53,
- };
-
- advertisement[5] = flags;
-
- assert_se(write(test_fd[1], advertisement, sizeof(advertisement)) ==
- sizeof(advertisement));
-
- if (verbose)
- printf(" sent RA with flag 0x%02x\n", flags);
-
- return 0;
-}
-
-int dhcp_network_icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) {
- return send_ra_function(0);
-}
-
-static void test_rs_done(sd_icmp6_nd *nd, int event, void *userdata) {
- sd_event *e = userdata;
- static int idx = 0;
- struct {
- uint8_t flag;
- int event;
- } flag_event[] = {
- { 0, SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_NONE },
- { ND_RA_FLAG_OTHER, SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_OTHER },
- { ND_RA_FLAG_MANAGED, SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_MANAGED }
- };
- uint32_t mtu;
-
- assert_se(nd);
-
- assert_se(event == flag_event[idx].event);
- idx++;
-
- if (verbose)
- printf(" got event %d\n", event);
-
- if (idx < 3) {
- send_ra(flag_event[idx].flag);
- return;
- }
-
- assert_se(sd_icmp6_ra_get_mtu(nd, &mtu) == -ENOMSG);
-
- sd_event_exit(e, 0);
-}
-
-static void test_rs(void) {
- sd_event *e;
- sd_icmp6_nd *nd;
- usec_t time_now = now(clock_boottime_or_monotonic());
-
- if (verbose)
- printf("* %s\n", __FUNCTION__);
-
- send_ra_function = send_ra;
-
- assert_se(sd_event_new(&e) >= 0);
-
- assert_se(sd_icmp6_nd_new(&nd) >= 0);
- assert_se(nd);
-
- assert_se(sd_icmp6_nd_attach_event(nd, e, 0) >= 0);
-
- assert_se(sd_icmp6_nd_set_index(nd, 42) >= 0);
- assert_se(sd_icmp6_nd_set_mac(nd, &mac_addr) >= 0);
- assert_se(sd_icmp6_nd_set_callback(nd, test_rs_done, e) >= 0);
-
- assert_se(sd_event_add_time(e, &test_hangcheck, clock_boottime_or_monotonic(),
- time_now + 2 *USEC_PER_SEC, 0,
- test_rs_hangcheck, NULL) >= 0);
-
- assert_se(sd_icmp6_nd_stop(nd) >= 0);
- assert_se(sd_icmp6_router_solicitation_start(nd) >= 0);
- assert_se(sd_icmp6_nd_stop(nd) >= 0);
-
- assert_se(sd_icmp6_router_solicitation_start(nd) >= 0);
-
- sd_event_loop(e);
-
- test_hangcheck = sd_event_source_unref(test_hangcheck);
-
- nd = sd_icmp6_nd_unref(nd);
- assert_se(!nd);
-
- close(test_fd[1]);
-
- sd_event_unref(e);
-}
-
-int main(int argc, char *argv[]) {
-
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
-
- test_rs();
- test_prefixes();
-
- return 0;
-}
diff --git a/src/libsystemd-network/test-ipv4ll-manual.c b/src/libsystemd-network/test-ipv4ll-manual.c
index dd2e44e7a3..599c789a26 100644
--- a/src/libsystemd-network/test-ipv4ll-manual.c
+++ b/src/libsystemd-network/test-ipv4ll-manual.c
@@ -19,21 +19,22 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
#include <errno.h>
+#include <net/if.h>
+#include <stdlib.h>
#include <unistd.h>
-
#include <linux/veth.h>
-#include <net/if.h>
#include "sd-event.h"
-#include "sd-netlink.h"
#include "sd-ipv4ll.h"
+#include "sd-netlink.h"
-#include "util.h"
-#include "event-util.h"
-#include "netlink-util.h"
+#include "alloc-util.h"
#include "in-addr-util.h"
+#include "netlink-util.h"
+#include "parse-util.h"
+#include "string-util.h"
+#include "util.h"
static void ll_handler(sd_ipv4ll *ll, int event, void *userdata) {
_cleanup_free_ char *address = NULL;
@@ -89,9 +90,9 @@ static int client_run(int ifindex, const char *seed_str, const struct ether_addr
}
static int test_ll(const char *ifname, const char *seed) {
- _cleanup_event_unref_ sd_event *e = NULL;
- _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
- _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL, *reply = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL, *reply = NULL;
struct ether_addr ha;
int ifindex;
diff --git a/src/libsystemd-network/test-ipv4ll.c b/src/libsystemd-network/test-ipv4ll.c
index e72204d992..bccab25cd4 100644
--- a/src/libsystemd-network/test-ipv4ll.c
+++ b/src/libsystemd-network/test-ipv4ll.c
@@ -18,20 +18,20 @@
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 <stdlib.h>
#include <sys/socket.h>
+#include <sys/types.h>
#include <unistd.h>
-#include "util.h"
-#include "socket-util.h"
-#include "event-util.h"
-
#include "sd-ipv4ll.h"
+
#include "arp-util.h"
+#include "fd-util.h"
+#include "socket-util.h"
+#include "util.h"
static bool verbose = false;
static bool extended = false;
@@ -100,6 +100,7 @@ int arp_network_bind_raw_socket(int index, be32_t address, const struct ether_ad
}
static void test_public_api_setters(sd_event *e) {
+ struct in_addr address = {};
unsigned seed = 0;
sd_ipv4ll *ll;
struct ether_addr mac_addr = {
@@ -118,6 +119,16 @@ static void test_public_api_setters(sd_event *e) {
assert_se(sd_ipv4ll_set_callback(NULL, NULL, NULL) == -EINVAL);
assert_se(sd_ipv4ll_set_callback(ll, NULL, NULL) == 0);
+ assert_se(sd_ipv4ll_set_address(ll, &address) == -EINVAL);
+ address.s_addr |= htobe32(169U << 24 | 254U << 16);
+ assert_se(sd_ipv4ll_set_address(ll, &address) == -EINVAL);
+ address.s_addr |= htobe32(0x00FF);
+ assert_se(sd_ipv4ll_set_address(ll, &address) == -EINVAL);
+ address.s_addr |= htobe32(0xF000);
+ assert_se(sd_ipv4ll_set_address(ll, &address) == 0);
+ address.s_addr |= htobe32(0x0F00);
+ assert_se(sd_ipv4ll_set_address(ll, &address) == -EINVAL);
+
assert_se(sd_ipv4ll_set_address_seed(NULL, seed) == -EINVAL);
assert_se(sd_ipv4ll_set_address_seed(ll, seed) == 0);
@@ -195,7 +206,7 @@ static void test_basic_request(sd_event *e) {
}
int main(int argc, char *argv[]) {
- _cleanup_event_unref_ sd_event *e = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
log_set_max_level(LOG_DEBUG);
log_parse_environment();
diff --git a/src/libsystemd-network/test-lldp.c b/src/libsystemd-network/test-lldp.c
index 06545aee59..497f3c1031 100644
--- a/src/libsystemd-network/test-lldp.c
+++ b/src/libsystemd-network/test-lldp.c
@@ -20,31 +20,40 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <arpa/inet.h>
+#include <net/ethernet.h>
#include <stdio.h>
#include <string.h>
-#include <net/ethernet.h>
-#include <arpa/inet.h>
-#include "macro.h"
-#include "lldp.h"
+#include "sd-event.h"
+#include "sd-lldp.h"
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "lldp-network.h"
#include "lldp-tlv.h"
+#include "lldp.h"
+#include "macro.h"
+#include "string-util.h"
#define TEST_LLDP_PORT "em1"
#define TEST_LLDP_TYPE_SYSTEM_NAME "systemd-lldp"
#define TEST_LLDP_TYPE_SYSTEM_DESC "systemd-lldp-desc"
+static int test_fd[2];
+
static struct ether_addr mac_addr = {
.ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
};
static int lldp_build_tlv_packet(tlv_packet **ret) {
- _cleanup_tlv_packet_free_ tlv_packet *m = NULL;
+ _cleanup_(sd_lldp_packet_unrefp) tlv_packet *m = NULL;
const uint8_t lldp_dst[] = LLDP_MULTICAST_ADDR;
struct ether_header ether = {
.ether_type = htons(ETHERTYPE_LLDP),
};
- /* Append ethernet header */
+ /* Append Ethernet header */
memcpy(&ether.ether_dhost, lldp_dst, ETHER_ADDR_LEN);
memcpy(&ether.ether_shost, &mac_addr, ETHER_ADDR_LEN);
@@ -202,6 +211,15 @@ static int lldp_parse_ttl_tlv(tlv_packet *m) {
return 0;
}
+static int lldp_get_destination_type(tlv_packet *m) {
+ int dest;
+
+ assert_se(sd_lldp_packet_get_destination_type(m, &dest) >= 0);
+ assert_se(dest == SD_LLDP_DESTINATION_TYPE_NEAREST_BRIDGE);
+
+ return 0;
+}
+
static int lldp_parse_tlv_packet(tlv_packet *m, int len) {
uint8_t subtype;
@@ -212,20 +230,241 @@ static int lldp_parse_tlv_packet(tlv_packet *m, int len) {
assert_se(lldp_parse_ttl_tlv(m) >= 0);
assert_se(lldp_parse_system_desc_tlv(m) >= 0);
+ assert_se(lldp_get_destination_type(m) >= 0);
+
return 0;
}
-int main(int argc, char *argv[]) {
- _cleanup_tlv_packet_free_ tlv_packet *tlv = NULL;
+static void test_parser(void) {
+ _cleanup_(sd_lldp_packet_unrefp) tlv_packet *tlv = NULL;
/* form a packet */
lldp_build_tlv_packet(&tlv);
-
/* parse the packet */
tlv_packet_parse_pdu(tlv, tlv->length);
-
/* verify */
lldp_parse_tlv_packet(tlv, tlv->length);
+}
+
+int lldp_network_bind_raw_socket(int ifindex) {
+ if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, test_fd) < 0)
+ return -errno;
+
+ return test_fd[0];
+}
+
+static int lldp_handler_calls;
+static void lldp_handler (sd_lldp *lldp, int event, void *userdata) {
+ lldp_handler_calls++;
+}
+
+static int start_lldp(sd_lldp **lldp, sd_event *e, sd_lldp_cb_t cb, void *cb_data) {
+ int r;
+
+ r = sd_lldp_new(42, "dummy", &mac_addr, lldp);
+ if (r)
+ return r;
+
+ r = sd_lldp_attach_event(*lldp, e, 0);
+ if (r)
+ return r;
+
+ r = sd_lldp_set_callback(*lldp, cb, cb_data);
+ if (r)
+ return r;
+
+ r = sd_lldp_start(*lldp);
+ if (r)
+ return r;
+
+ return 0;
+}
+
+static int stop_lldp(sd_lldp *lldp) {
+ int r;
+
+ r = sd_lldp_stop(lldp);
+ if (r)
+ return r;
+
+ r = sd_lldp_detach_event(lldp);
+ if (r)
+ return r;
+
+ sd_lldp_unref(lldp);
+ safe_close(test_fd[1]);
+
+ return 0;
+}
+
+static void test_receive_basic_packet(sd_event *e) {
+ sd_lldp *lldp;
+ sd_lldp_packet **packets;
+ uint8_t type, *data;
+ uint16_t length, ttl;
+ int dest_type;
+ char *str;
+ uint8_t frame[] = {
+ /* Ethernet header */
+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC*/
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
+ 0x88, 0xcc, /* Ethertype */
+ /* LLDP mandatory TLVs */
+ 0x02, 0x07, 0x04, 0x00, 0x01, 0x02, /* Chassis: MAC, 00:01:02:03:04:05 */
+ 0x03, 0x04, 0x05,
+ 0x04, 0x04, 0x05, 0x31, 0x2f, 0x33, /* Port: interface name, "1/3" */
+ 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds*/
+ /* LLDP optional TLVs */
+ 0x08, 0x04, 0x50, 0x6f, 0x72, 0x74, /* Port Description: "Port" */
+ 0x0a, 0x03, 0x53, 0x59, 0x53, /* System Name: "SYS" */
+ 0x0c, 0x04, 0x66, 0x6f, 0x6f, 0x00, /* System Description: "foo" (NULL-terminated) */
+ 0x00, 0x00 /* End Of LLDPDU */
+ };
+
+ lldp_handler_calls = 0;
+ assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0);
+
+ assert_se(write(test_fd[1], frame, sizeof(frame)) == sizeof(frame));
+ sd_event_run(e, 0);
+ assert_se(lldp_handler_calls == 1);
+ assert_se(sd_lldp_get_packets(lldp, &packets) == 1);
+
+ assert_se(sd_lldp_packet_read_chassis_id(packets[0], &type, &data, &length) == 0);
+ assert_se(type == LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS);
+ assert_se(length == ETH_ALEN);
+ assert_se(!memcmp(data, "\x00\x01\x02\x03\x04\x05", ETH_ALEN));
+
+ assert_se(sd_lldp_packet_read_port_id(packets[0], &type, &data, &length) == 0);
+ assert_se(type == LLDP_PORT_SUBTYPE_INTERFACE_NAME);
+ assert_se(length == 3);
+ assert_se(strneq((char *) data, "1/3", 3));
+
+ assert_se(sd_lldp_packet_read_port_description(packets[0], &str, &length) == 0);
+ assert_se(length == 4);
+ assert_se(strneq(str, "Port", 4));
+
+ assert_se(sd_lldp_packet_read_system_name(packets[0], &str, &length) == 0);
+ assert_se(length == 3);
+ assert_se(strneq(str, "SYS", 3));
+
+ assert_se(sd_lldp_packet_read_system_description(packets[0], &str, &length) == 0);
+ assert_se(length == 4); /* This is the real length in the TLV packet */
+ assert_se(strneq(str, "foo", 3));
+
+ assert_se(sd_lldp_packet_read_ttl(packets[0], &ttl) == 0);
+ assert_se(ttl == 120);
+
+ assert_se(sd_lldp_packet_get_destination_type(packets[0], &dest_type) == 0);
+ assert_se(dest_type == SD_LLDP_DESTINATION_TYPE_NEAREST_NON_TPMR_BRIDGE);
+
+ sd_lldp_packet_unref(packets[0]);
+ free(packets);
+
+ assert_se(stop_lldp(lldp) == 0);
+}
+
+static void test_receive_incomplete_packet(sd_event *e) {
+ sd_lldp *lldp;
+ sd_lldp_packet **packets;
+ uint8_t frame[] = {
+ /* Ethernet header */
+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC*/
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
+ 0x88, 0xcc, /* Ethertype */
+ /* LLDP mandatory TLVs */
+ 0x02, 0x07, 0x04, 0x00, 0x01, 0x02, /* Chassis: MAC, 00:01:02:03:04:05 */
+ 0x03, 0x04, 0x05,
+ 0x04, 0x04, 0x05, 0x31, 0x2f, 0x33, /* Port: interface name, "1/3" */
+ /* Missing TTL */
+ 0x00, 0x00 /* End Of LLDPDU */
+ };
+
+ lldp_handler_calls = 0;
+ assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0);
+
+ assert_se(write(test_fd[1], frame, sizeof(frame)) == sizeof(frame));
+ sd_event_run(e, 0);
+ assert_se(lldp_handler_calls == 0);
+ assert_se(sd_lldp_get_packets(lldp, &packets) == 0);
+
+ assert_se(stop_lldp(lldp) == 0);
+}
+
+static void test_receive_oui_packet(sd_event *e) {
+ sd_lldp *lldp;
+ sd_lldp_packet **packets;
+ uint32_t id32;
+ uint16_t id16, len;
+ uint8_t flags;
+ char *str;
+ uint8_t frame[] = {
+ /* Ethernet header */
+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC*/
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
+ 0x88, 0xcc, /* Ethertype */
+ /* LLDP mandatory TLVs */
+ 0x02, 0x07, 0x04, 0x00, 0x01, 0x02, /* Chassis: MAC, 00:01:02:03:04:05 */
+ 0x03, 0x04, 0x05,
+ 0x04, 0x04, 0x05, 0x31, 0x2f, 0x33, /* Port TLV: interface name, "1/3" */
+ 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds*/
+ /* LLDP optional TLVs */
+ 0xfe, 0x06, 0x00, 0x80, 0xc2, 0x01, /* Port VLAN ID: 0x1234 */
+ 0x12, 0x34,
+ 0xfe, 0x07, 0x00, 0x80, 0xc2, 0x02, /* Port and protocol: flag 1, PPVID 0x7788 */
+ 0x01, 0x77, 0x88,
+ 0xfe, 0x0d, 0x00, 0x80, 0xc2, 0x03, /* VLAN Name: ID 0x1234, name "Vlan51" */
+ 0x12, 0x34, 0x06, 0x56, 0x6c, 0x61,
+ 0x6e, 0x35, 0x31,
+ 0xfe, 0x06, 0x00, 0x80, 0xc2, 0x06, /* Management VID: 0x0102 */
+ 0x01, 0x02,
+ 0xfe, 0x09, 0x00, 0x80, 0xc2, 0x07, /* Link aggregation: status 1, ID 0x00140012 */
+ 0x01, 0x00, 0x14, 0x00, 0x12,
+ 0x00, 0x00 /* End of LLDPDU */
+ };
+
+ lldp_handler_calls = 0;
+ assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0);
+
+ assert_se(write(test_fd[1], frame, sizeof(frame)) == sizeof(frame));
+ sd_event_run(e, 0);
+ assert_se(lldp_handler_calls == 1);
+ assert_se(sd_lldp_get_packets(lldp, &packets) == 1);
+
+ assert_se(sd_lldp_packet_read_port_vlan_id(packets[0], &id16) == 0);
+ assert_se(id16 == 0x1234);
+
+ assert_se(sd_lldp_packet_read_port_protocol_vlan_id(packets[0], &flags, &id16) == 0);
+ assert_se(flags == 1);
+ assert_se(id16 == 0x7788);
+
+ assert_se(sd_lldp_packet_read_vlan_name(packets[0], &id16, &str, &len) == 0);
+ assert_se(id16 == 0x1234);
+ assert_se(len == 6);
+ assert_se(strneq(str, "Vlan51", 6));
+
+ assert_se(sd_lldp_packet_read_management_vid(packets[0], &id16) == 0);
+ assert_se(id16 == 0x0102);
+
+ assert_se(sd_lldp_packet_read_link_aggregation(packets[0], &flags, &id32) == 0);
+ assert_se(flags == 1);
+ assert_se(id32 == 0x00140012);
+
+ sd_lldp_packet_unref(packets[0]);
+ free(packets);
+
+ assert_se(stop_lldp(lldp) == 0);
+}
+
+int main(int argc, char *argv[]) {
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+
+ test_parser();
+
+ /* LLDP reception tests */
+ assert_se(sd_event_new(&e) == 0);
+ test_receive_basic_packet(e);
+ test_receive_incomplete_packet(e);
+ test_receive_oui_packet(e);
return 0;
}
diff --git a/src/libsystemd-network/test-ndisc-rs.c b/src/libsystemd-network/test-ndisc-rs.c
new file mode 100644
index 0000000000..a485be704e
--- /dev/null
+++ b/src/libsystemd-network/test-ndisc-rs.c
@@ -0,0 +1,170 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Intel Corporation. All rights reserved.
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General 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 <netinet/icmp6.h>
+
+#include "sd-ndisc.h"
+
+#include "icmp6-util.h"
+#include "socket-util.h"
+
+static struct ether_addr mac_addr = {
+ .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
+};
+
+static bool verbose = false;
+static sd_event_source *test_hangcheck;
+static int test_fd[2];
+
+typedef int (*send_ra_t)(uint8_t flags);
+static send_ra_t send_ra_function;
+
+static int test_rs_hangcheck(sd_event_source *s, uint64_t usec,
+ void *userdata) {
+ assert_se(false);
+
+ return 0;
+}
+
+int icmp6_bind_router_solicitation(int index) {
+ assert_se(index == 42);
+
+ if (socketpair(AF_UNIX, SOCK_DGRAM, 0, test_fd) < 0)
+ return -errno;
+
+ return test_fd[0];
+}
+
+static int send_ra(uint8_t flags) {
+ uint8_t advertisement[] = {
+ 0x86, 0x00, 0xde, 0x83, 0x40, 0xc0, 0x00, 0xb4,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x01, 0xf4,
+ 0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
+ 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x1f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
+ 0x03, 0x6c, 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74,
+ 0x72, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x01, 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53,
+ };
+
+ advertisement[5] = flags;
+
+ assert_se(write(test_fd[1], advertisement, sizeof(advertisement)) ==
+ sizeof(advertisement));
+
+ if (verbose)
+ printf(" sent RA with flag 0x%02x\n", flags);
+
+ return 0;
+}
+
+int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) {
+ return send_ra_function(0);
+}
+
+static void test_rs_done(sd_ndisc *nd, uint8_t flags, const struct in6_addr *gateway, unsigned lifetime, int pref, void *userdata) {
+ sd_event *e = userdata;
+ static unsigned idx = 0;
+ uint8_t flags_array[] = {
+ 0,
+ 0,
+ 0,
+ ND_RA_FLAG_OTHER,
+ ND_RA_FLAG_MANAGED
+ };
+ uint32_t mtu;
+
+ assert_se(nd);
+
+ assert_se(flags == flags_array[idx]);
+ idx++;
+
+ if (verbose)
+ printf(" got event 0x%02x\n", flags);
+
+ if (idx < ELEMENTSOF(flags_array)) {
+ send_ra(flags_array[idx]);
+ return;
+ }
+
+ assert_se(sd_ndisc_get_mtu(nd, &mtu) == -ENOMSG);
+
+ sd_event_exit(e, 0);
+}
+
+static void test_rs(void) {
+ sd_event *e;
+ sd_ndisc *nd;
+ usec_t time_now = now(clock_boottime_or_monotonic());
+
+ if (verbose)
+ printf("* %s\n", __FUNCTION__);
+
+ send_ra_function = send_ra;
+
+ assert_se(sd_event_new(&e) >= 0);
+
+ assert_se(sd_ndisc_new(&nd) >= 0);
+ assert_se(nd);
+
+ assert_se(sd_ndisc_attach_event(nd, e, 0) >= 0);
+
+ assert_se(sd_ndisc_set_index(nd, 42) >= 0);
+ assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0);
+ assert_se(sd_ndisc_set_callback(nd, test_rs_done, NULL, NULL, NULL, e) >= 0);
+
+ assert_se(sd_event_add_time(e, &test_hangcheck, clock_boottime_or_monotonic(),
+ time_now + 2 *USEC_PER_SEC, 0,
+ test_rs_hangcheck, NULL) >= 0);
+
+ assert_se(sd_ndisc_stop(nd) >= 0);
+ assert_se(sd_ndisc_router_discovery_start(nd) >= 0);
+ assert_se(sd_ndisc_stop(nd) >= 0);
+
+ assert_se(sd_ndisc_router_discovery_start(nd) >= 0);
+
+ sd_event_loop(e);
+
+ test_hangcheck = sd_event_source_unref(test_hangcheck);
+
+ nd = sd_ndisc_unref(nd);
+ assert_se(!nd);
+
+ close(test_fd[1]);
+
+ sd_event_unref(e);
+}
+
+int main(int argc, char *argv[]) {
+
+ log_set_max_level(LOG_DEBUG);
+ log_parse_environment();
+ log_open();
+
+ test_rs();
+
+ return 0;
+}
diff --git a/src/libsystemd-network/test-pppoe.c b/src/libsystemd-network/test-pppoe.c
deleted file mode 100644
index 6d71569a26..0000000000
--- a/src/libsystemd-network/test-pppoe.c
+++ /dev/null
@@ -1,176 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 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 <stdlib.h>
-#include <errno.h>
-#include <unistd.h>
-
-#include <linux/veth.h>
-#include <net/if.h>
-
-#include "util.h"
-#include "sd-event.h"
-#include "event-util.h"
-#include "sd-netlink.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;
- sd_event *e = userdata;
-
- assert_se(ppp);
- assert_se(e);
-
- switch (event) {
- case SD_PPPOE_EVENT_RUNNING:
- assert_se(pppoe_state == -1);
- log_info("running");
- break;
- case SD_PPPOE_EVENT_STOPPED:
- assert_se(pppoe_state == SD_PPPOE_EVENT_RUNNING);
- log_info("stopped");
- assert_se(sd_event_exit(e, 0) >= 0);
- break;
- default:
- assert_not_reached("invalid pppoe event");
- }
-
- pppoe_state = event;
-}
-
-static int client_run(const char *client_name, sd_event *e) {
- sd_pppoe *pppoe;
- int client_ifindex;
-
- client_ifindex = (int) if_nametoindex(client_name);
- assert_se(client_ifindex > 0);
-
- assert_se(sd_pppoe_new(&pppoe) >= 0);
- assert_se(sd_pppoe_attach_event(pppoe, e, 0) >= 0);
-
- assert_se(sd_pppoe_set_ifname(pppoe, "pppoe-client") >= 0);
- assert_se(sd_pppoe_set_ifindex(pppoe, client_ifindex) >= 0);
- assert_se(sd_pppoe_set_callback(pppoe, pppoe_handler, e) >= 0);
-
- log_info("starting PPPoE client, it will exit when the server times out and sends PADT");
-
- assert_se(sd_pppoe_start(pppoe) >= 0);
-
- assert_se(sd_event_loop(e) >= 0);
-
- assert_se(!sd_pppoe_unref(pppoe));
-
- return EXIT_SUCCESS;
-}
-
-static int test_pppoe_server(sd_event *e) {
- sd_netlink *rtnl;
- sd_netlink_message *m;
- pid_t pid;
- int r, client_ifindex, server_ifindex;
-
- r = unshare(CLONE_NEWNET);
- if (r < 0 && errno == EPERM)
- return EXIT_TEST_SKIP;
-
- assert_se(r >= 0);
-
- assert_se(sd_netlink_open(&rtnl) >= 0);
- assert_se(sd_netlink_attach_event(rtnl, e, 0) >= 0);
-
- assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0) >= 0);
- assert_se(sd_netlink_message_append_string(m, IFLA_IFNAME, "pppoe-server") >= 0);
- assert_se(sd_netlink_message_open_container(m, IFLA_LINKINFO) >= 0);
- assert_se(sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "veth") >= 0);
- assert_se(sd_netlink_message_open_container(m, VETH_INFO_PEER) >= 0);
- assert_se(sd_netlink_message_append_string(m, IFLA_IFNAME, "pppoe-client") >= 0);
- assert_se(sd_netlink_message_close_container(m) >= 0);
- assert_se(sd_netlink_message_close_container(m) >= 0);
- assert_se(sd_netlink_message_close_container(m) >= 0);
- assert_se(sd_netlink_call(rtnl, m, 0, NULL) >= 0);
-
- client_ifindex = (int) if_nametoindex("pppoe-client");
- assert_se(client_ifindex > 0);
- server_ifindex = (int) if_nametoindex("pppoe-server");
- assert_se(server_ifindex > 0);
-
- m = sd_netlink_message_unref(m);
- assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_SETLINK, client_ifindex) >= 0);
- assert_se(sd_rtnl_message_link_set_flags(m, IFF_UP, IFF_UP) >= 0);
- assert_se(sd_netlink_call(rtnl, m, 0, NULL) >= 0);
-
- m = sd_netlink_message_unref(m);
- assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_SETLINK, server_ifindex) >= 0);
- assert_se(sd_rtnl_message_link_set_flags(m, IFF_UP, IFF_UP) >= 0);
- assert_se(sd_netlink_call(rtnl, m, 0, NULL) >= 0);
-
- pid = fork();
- assert_se(pid >= 0);
- if (pid == 0) {
- /* let the client send some discover messages before the server is started */
- sleep(2);
-
- /* TODO: manage pppoe-server-options */
- execlp("pppoe-server", "pppoe-server", "-F",
- "-I", "pppoe-server",
- "-C", "Test-AC",
- "-S", "Service-Default",
- "-S", "Service-First-Auxiliary",
- "-S", "Service-Second-Auxiliary",
- NULL);
- assert_not_reached("failed to execute pppoe-server. not installed?");
- }
-
- client_run("pppoe-client", e);
-
- assert_se(kill(pid, SIGTERM) >= 0);
- assert_se(wait_for_terminate(pid, NULL) >= 0);
-
- assert_se(!sd_netlink_message_unref(m));
- assert_se(!sd_netlink_unref(rtnl));
-
- return EXIT_SUCCESS;
-}
-
-int main(int argc, char *argv[]) {
- _cleanup_event_unref_ sd_event *e = NULL;
-
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
-
- assert_se(sd_event_new(&e) >= 0);
-
- if (argc == 1) {
- log_info("running PPPoE client against local server");
-
- return test_pppoe_server(e);
- } else if (argc == 2) {
- log_info("running PPPoE client over '%s'", argv[1]);
-
- return client_run(argv[1], e);
- } else {
- log_error("This program takes one or no arguments.\n"
- "\t %s [<ifname>]", program_invocation_short_name);
- return EXIT_FAILURE;
- }
-}
diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym
index 518cbbb7ed..043ff13e6f 100644
--- a/src/libsystemd/libsystemd.sym
+++ b/src/libsystemd/libsystemd.sym
@@ -477,4 +477,7 @@ global:
LIBSYSTEMD_227 {
global:
sd_bus_default_flush_close;
+ sd_bus_path_decode_many;
+ sd_bus_path_encode_many;
+ sd_listen_fds_with_names;
} LIBSYSTEMD_226;
diff --git a/src/libsystemd/sd-bus/bus-bloom.c b/src/libsystemd/sd-bus/bus-bloom.c
index 91fab90cb0..c0c5d445eb 100644
--- a/src/libsystemd/sd-bus/bus-bloom.c
+++ b/src/libsystemd/sd-bus/bus-bloom.c
@@ -19,9 +19,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
-#include "siphash24.h"
#include "bus-bloom.h"
+#include "siphash24.h"
+#include "util.h"
static inline void set_bit(uint64_t filter[], unsigned long b) {
filter[b >> 6] |= 1ULL << (b & 63);
@@ -45,7 +45,7 @@ static void bloom_add_data(
const void *data, /* Data to hash */
size_t n) { /* Size of data to hash in bytes */
- uint8_t h[8];
+ uint64_t h;
uint64_t m;
unsigned w, i, c = 0;
unsigned hash_index;
@@ -72,11 +72,11 @@ static void bloom_add_data(
for (d = 0; d < w; d++) {
if (c <= 0) {
- siphash24(h, data, n, hash_keys[hash_index++].bytes);
+ h = siphash24(data, n, hash_keys[hash_index++].bytes);
c += 8;
}
- p = (p << 8ULL) | (uint64_t) h[8 - c];
+ p = (p << 8ULL) | (uint64_t) ((uint8_t *)&h)[8 - c];
c--;
}
diff --git a/src/libsystemd/sd-bus/bus-bloom.h b/src/libsystemd/sd-bus/bus-bloom.h
index a9350d7f51..38892044f1 100644
--- a/src/libsystemd/sd-bus/bus-bloom.h
+++ b/src/libsystemd/sd-bus/bus-bloom.h
@@ -22,6 +22,7 @@
***/
#include <stdbool.h>
+#include <stddef.h>
#include <stdint.h>
/*
diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c
index 52f8dfd3be..9ddc9b5aaf 100644
--- a/src/libsystemd/sd-bus/bus-common-errors.c
+++ b/src/libsystemd/sd-bus/bus-common-errors.c
@@ -22,8 +22,9 @@
#include <errno.h>
#include "sd-bus.h"
-#include "bus-error.h"
+
#include "bus-common-errors.h"
+#include "bus-error.h"
BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_UNIT, ENOENT),
@@ -66,12 +67,19 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_PROCESS, ESRCH),
- SD_BUS_ERROR_MAP(BUS_ERROR_NO_NAME_SERVERS, EIO),
+ SD_BUS_ERROR_MAP(BUS_ERROR_NO_NAME_SERVERS, ESRCH),
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, EDEADLK),
SD_BUS_ERROR_MAP(BUS_ERROR_ABORTED, ECANCELED),
+ SD_BUS_ERROR_MAP(BUS_ERROR_CONNECTION_FAILURE, ECONNREFUSED),
+ SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_SERVICE, EUNATCH),
+ SD_BUS_ERROR_MAP(BUS_ERROR_DNSSEC_FAILED, EHOSTUNREACH),
+ SD_BUS_ERROR_MAP(BUS_ERROR_NO_TRUST_ANCHOR, EHOSTUNREACH),
+ SD_BUS_ERROR_MAP(BUS_ERROR_RR_TYPE_UNSUPPORTED, EOPNOTSUPP),
+ SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_LINK, ENXIO),
+ SD_BUS_ERROR_MAP(BUS_ERROR_LINK_BUSY, EBUSY),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_TRANSFER, ENXIO),
SD_BUS_ERROR_MAP(BUS_ERROR_TRANSFER_IN_PROGRESS, EBUSY),
diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h
index f2092795f4..e93b6ac448 100644
--- a/src/libsystemd/sd-bus/bus-common-errors.h
+++ b/src/libsystemd/sd-bus/bus-common-errors.h
@@ -72,6 +72,13 @@
#define BUS_ERROR_NO_RESOURCES "org.freedesktop.resolve1.NoResources"
#define BUS_ERROR_CNAME_LOOP "org.freedesktop.resolve1.CNameLoop"
#define BUS_ERROR_ABORTED "org.freedesktop.resolve1.Aborted"
+#define BUS_ERROR_CONNECTION_FAILURE "org.freedesktop.resolve1.ConnectionFailure"
+#define BUS_ERROR_NO_SUCH_SERVICE "org.freedesktop.resolve1.NoSuchService"
+#define BUS_ERROR_DNSSEC_FAILED "org.freedesktop.resolve1.DnssecFailed"
+#define BUS_ERROR_NO_TRUST_ANCHOR "org.freedesktop.resolve1.NoTrustAnchor"
+#define BUS_ERROR_RR_TYPE_UNSUPPORTED "org.freedesktop.resolve1.ResourceRecordTypeUnsupported"
+#define BUS_ERROR_NO_SUCH_LINK "org.freedesktop.resolve1.NoSuchLink"
+#define BUS_ERROR_LINK_BUSY "org.freedesktop.resolve1.LinkBusy"
#define _BUS_ERROR_DNS "org.freedesktop.resolve1.DnsError."
#define BUS_ERROR_NO_SUCH_TRANSFER "org.freedesktop.import1.NoSuchTransfer"
diff --git a/src/libsystemd/sd-bus/bus-container.c b/src/libsystemd/sd-bus/bus-container.c
index 5c607f49b1..7da6ba9903 100644
--- a/src/libsystemd/sd-bus/bus-container.c
+++ b/src/libsystemd/sd-bus/bus-container.c
@@ -19,14 +19,15 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
#include <fcntl.h>
+#include <unistd.h>
-#include "util.h"
-#include "process-util.h"
+#include "bus-container.h"
#include "bus-internal.h"
#include "bus-socket.h"
-#include "bus-container.h"
+#include "fd-util.h"
+#include "process-util.h"
+#include "util.h"
int bus_container_connect_socket(sd_bus *b) {
_cleanup_close_pair_ int pair[2] = { -1, -1 };
@@ -217,15 +218,8 @@ int bus_container_connect_kernel(sd_bus *b) {
_exit(EXIT_FAILURE);
}
- cmsg = CMSG_FIRSTHDR(&mh);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(sizeof(int));
- memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
-
- mh.msg_controllen = cmsg->cmsg_len;
-
- if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
+ r = send_one_fd(pair[1], fd, 0);
+ if (r < 0)
_exit(EXIT_FAILURE);
_exit(EXIT_SUCCESS);
diff --git a/src/libsystemd/sd-bus/bus-control.c b/src/libsystemd/sd-bus/bus-control.c
index aeb48bedd1..ff628cfe72 100644
--- a/src/libsystemd/sd-bus/bus-control.c
+++ b/src/libsystemd/sd-bus/bus-control.c
@@ -23,17 +23,22 @@
#include <valgrind/memcheck.h>
#endif
-#include <stddef.h>
#include <errno.h>
+#include <stddef.h>
-#include "strv.h"
#include "sd-bus.h"
+
+#include "alloc-util.h"
+#include "bus-bloom.h"
+#include "bus-control.h"
#include "bus-internal.h"
#include "bus-message.h"
-#include "bus-control.h"
-#include "bus-bloom.h"
#include "bus-util.h"
-#include "capability.h"
+#include "capability-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "user-util.h"
_public_ int sd_bus_get_unique_name(sd_bus *bus, const char **unique) {
int r;
@@ -86,7 +91,7 @@ static int bus_request_name_kernel(sd_bus *bus, const char *name, uint64_t flags
}
static int bus_request_name_dbus1(sd_bus *bus, const char *name, uint64_t flags) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
uint32_t ret, param = 0;
int r;
@@ -182,7 +187,7 @@ static int bus_release_name_kernel(sd_bus *bus, const char *name) {
}
static int bus_release_name_dbus1(sd_bus *bus, const char *name) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
uint32_t ret;
int r;
@@ -321,7 +326,7 @@ static int bus_list_names_kernel(sd_bus *bus, char ***acquired, char ***activata
}
static int bus_list_names_dbus1(sd_bus *bus, char ***acquired, char ***activatable) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_strv_free_ char **x = NULL, **y = NULL;
int r;
@@ -642,7 +647,7 @@ int bus_get_name_creds_kdbus(
bool allow_activator,
sd_bus_creds **creds) {
- _cleanup_bus_creds_unref_ sd_bus_creds *c = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL;
struct kdbus_cmd_info *cmd;
struct kdbus_info *conn_info;
size_t size, l;
@@ -748,8 +753,8 @@ static int bus_get_name_creds_dbus1(
uint64_t mask,
sd_bus_creds **creds) {
- _cleanup_bus_message_unref_ sd_bus_message *reply_unique = NULL, *reply = NULL;
- _cleanup_bus_creds_unref_ sd_bus_creds *c = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply_unique = NULL, *reply = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL;
const char *unique = NULL;
pid_t pid = 0;
int r;
@@ -853,7 +858,7 @@ static int bus_get_name_creds_dbus1(
}
if (mask & SD_BUS_CREDS_SELINUX_CONTEXT) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
const void *p = NULL;
size_t sz = 0;
@@ -925,7 +930,7 @@ _public_ int sd_bus_get_name_creds(
}
static int bus_get_owner_creds_kdbus(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) {
- _cleanup_bus_creds_unref_ sd_bus_creds *c = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL;
struct kdbus_cmd_info cmd = {
.size = sizeof(struct kdbus_cmd_info),
};
@@ -974,10 +979,14 @@ static int bus_get_owner_creds_kdbus(sd_bus *bus, uint64_t mask, sd_bus_creds **
}
static int bus_get_owner_creds_dbus1(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) {
- _cleanup_bus_creds_unref_ sd_bus_creds *c = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL;
pid_t pid = 0;
+ bool do_label;
int r;
- bool do_label = bus->label && (mask & SD_BUS_CREDS_SELINUX_CONTEXT);
+
+ assert(bus);
+
+ do_label = bus->label && (mask & SD_BUS_CREDS_SELINUX_CONTEXT);
/* Avoid allocating anything if we have no chance of returning useful data */
if (!bus->ucred_valid && !do_label)
@@ -1534,7 +1543,7 @@ int bus_remove_match_internal(
}
_public_ int sd_bus_get_name_machine_id(sd_bus *bus, const char *name, sd_id128_t *machine) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL;
const char *mid;
int r;
diff --git a/src/libsystemd/sd-bus/bus-control.h b/src/libsystemd/sd-bus/bus-control.h
index 5009ca8e61..e01b075832 100644
--- a/src/libsystemd/sd-bus/bus-control.h
+++ b/src/libsystemd/sd-bus/bus-control.h
@@ -22,6 +22,7 @@
***/
#include "sd-bus.h"
+
#include "bus-match.h"
int bus_add_match_internal(sd_bus *bus, const char *match, struct bus_match_component *components, unsigned n_components, uint64_t cookie);
diff --git a/src/libsystemd/sd-bus/bus-convenience.c b/src/libsystemd/sd-bus/bus-convenience.c
index af5f7da11c..d3d5149242 100644
--- a/src/libsystemd/sd-bus/bus-convenience.c
+++ b/src/libsystemd/sd-bus/bus-convenience.c
@@ -22,8 +22,9 @@
#include "bus-internal.h"
#include "bus-message.h"
#include "bus-signature.h"
-#include "bus-util.h"
#include "bus-type.h"
+#include "bus-util.h"
+#include "string-util.h"
_public_ int sd_bus_emit_signal(
sd_bus *bus,
@@ -32,7 +33,7 @@ _public_ int sd_bus_emit_signal(
const char *member,
const char *types, ...) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
int r;
assert_return(bus, -EINVAL);
@@ -69,7 +70,7 @@ _public_ int sd_bus_call_method_async(
void *userdata,
const char *types, ...) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
int r;
assert_return(bus, -EINVAL);
@@ -105,7 +106,7 @@ _public_ int sd_bus_call_method(
sd_bus_message **reply,
const char *types, ...) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
int r;
bus_assert_return(bus, -EINVAL, error);
@@ -140,7 +141,7 @@ _public_ int sd_bus_reply_method_return(
sd_bus_message *call,
const char *types, ...) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
int r;
assert_return(call, -EINVAL);
@@ -176,7 +177,7 @@ _public_ int sd_bus_reply_method_error(
sd_bus_message *call,
const sd_bus_error *e) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
int r;
assert_return(call, -EINVAL);
@@ -205,7 +206,7 @@ _public_ int sd_bus_reply_method_errorf(
const char *format,
...) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
va_list ap;
assert_return(call, -EINVAL);
@@ -232,7 +233,7 @@ _public_ int sd_bus_reply_method_errno(
int error,
const sd_bus_error *p) {
- _cleanup_bus_error_free_ sd_bus_error berror = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error berror = SD_BUS_ERROR_NULL;
assert_return(call, -EINVAL);
assert_return(call->sealed, -EPERM);
@@ -260,7 +261,7 @@ _public_ int sd_bus_reply_method_errnof(
const char *format,
...) {
- _cleanup_bus_error_free_ sd_bus_error berror = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error berror = SD_BUS_ERROR_NULL;
va_list ap;
assert_return(call, -EINVAL);
@@ -333,7 +334,7 @@ _public_ int sd_bus_get_property_trivial(
sd_bus_error *error,
char type, void *ptr) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
int r;
bus_assert_return(bus, -EINVAL, error);
@@ -375,7 +376,7 @@ _public_ int sd_bus_get_property_string(
sd_bus_error *error,
char **ret) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
const char *s;
char *n;
int r;
@@ -425,7 +426,7 @@ _public_ int sd_bus_get_property_strv(
sd_bus_error *error,
char ***ret) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
int r;
bus_assert_return(bus, -EINVAL, error);
@@ -466,7 +467,7 @@ _public_ int sd_bus_set_property(
sd_bus_error *error,
const char *type, ...) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
va_list ap;
int r;
@@ -554,7 +555,7 @@ _public_ int sd_bus_query_sender_creds(sd_bus_message *call, uint64_t mask, sd_b
}
_public_ int sd_bus_query_sender_privilege(sd_bus_message *call, int capability) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
uid_t our_uid;
bool know_caps = false;
int r;
diff --git a/src/libsystemd/sd-bus/bus-creds.c b/src/libsystemd/sd-bus/bus-creds.c
index 3e8cb0b7d0..4e0d02a62c 100644
--- a/src/libsystemd/sd-bus/bus-creds.c
+++ b/src/libsystemd/sd-bus/bus-creds.c
@@ -19,22 +19,28 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
#include <linux/capability.h>
+#include <stdlib.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 "alloc-util.h"
+#include "audit-util.h"
+#include "bus-creds.h"
+#include "bus-label.h"
#include "bus-message.h"
#include "bus-util.h"
+#include "capability-util.h"
+#include "cgroup-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "formats-util.h"
+#include "hexdecoct.h"
+#include "parse-util.h"
+#include "process-util.h"
+#include "string-util.h"
#include "strv.h"
-#include "bus-creds.h"
-#include "bus-label.h"
+#include "terminal-util.h"
+#include "user-util.h"
+#include "util.h"
enum {
CAP_OFFSET_INHERITABLE = 0,
@@ -69,7 +75,9 @@ void bus_creds_done(sd_bus_creds *c) {
}
_public_ sd_bus_creds *sd_bus_creds_ref(sd_bus_creds *c) {
- assert_return(c, NULL);
+
+ if (!c)
+ return NULL;
if (c->allocated) {
assert(c->n_ref > 0);
@@ -1112,7 +1120,7 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) {
}
int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret) {
- _cleanup_bus_creds_unref_ sd_bus_creds *n = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *n = NULL;
int r;
assert(c);
diff --git a/src/libsystemd/sd-bus/bus-dump.c b/src/libsystemd/sd-bus/bus-dump.c
index 8833b9c677..43a7e67a6d 100644
--- a/src/libsystemd/sd-bus/bus-dump.c
+++ b/src/libsystemd/sd-bus/bus-dump.c
@@ -19,18 +19,21 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
-#include "capability.h"
-#include "strv.h"
-#include "macro.h"
+#include "alloc-util.h"
+#include "bus-dump.h"
+#include "bus-internal.h"
+#include "bus-message.h"
+#include "bus-type.h"
#include "cap-list.h"
+#include "capability-util.h"
+#include "fileio.h"
#include "formats-util.h"
+#include "locale-util.h"
+#include "macro.h"
+#include "string-util.h"
+#include "strv.h"
#include "terminal-util.h"
-
-#include "bus-message.h"
-#include "bus-internal.h"
-#include "bus-type.h"
-#include "bus-dump.h"
+#include "util.h"
static char *indent(unsigned level, unsigned flags) {
char *p;
diff --git a/src/libsystemd/sd-bus/bus-dump.h b/src/libsystemd/sd-bus/bus-dump.h
index d2522edeba..71e56991fa 100644
--- a/src/libsystemd/sd-bus/bus-dump.h
+++ b/src/libsystemd/sd-bus/bus-dump.h
@@ -21,8 +21,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
#include <stdbool.h>
+#include <stdio.h>
#include "sd-bus.h"
diff --git a/src/libsystemd/sd-bus/bus-error.c b/src/libsystemd/sd-bus/bus-error.c
index 64a5a972ae..c77eb5fd03 100644
--- a/src/libsystemd/sd-bus/bus-error.c
+++ b/src/libsystemd/sd-bus/bus-error.c
@@ -20,17 +20,19 @@
***/
#include <errno.h>
-#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
-#include <string.h>
#include <stdio.h>
-
-#include "util.h"
-#include "errno-list.h"
+#include <stdlib.h>
+#include <string.h>
#include "sd-bus.h"
+
+#include "alloc-util.h"
#include "bus-error.h"
+#include "errno-list.h"
+#include "string-util.h"
+#include "util.h"
BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_standard_errors[] = {
SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Failed", EACCES),
@@ -91,14 +93,14 @@ static int bus_error_name_to_errno(const char *name) {
p = startswith(name, "System.Error.");
if (p) {
r = errno_from_name(p);
- if (r <= 0)
+ if (r < 0)
return EIO;
return r;
}
- if (additional_error_maps) {
- for (map = additional_error_maps; *map; map++) {
+ if (additional_error_maps)
+ for (map = additional_error_maps; *map; map++)
for (m = *map;; m++) {
/* For additional error maps the end marker is actually the end marker */
if (m->code == BUS_ERROR_MAP_END_MARKER)
@@ -107,15 +109,13 @@ static int bus_error_name_to_errno(const char *name) {
if (streq(m->name, name))
return m->code;
}
- }
- }
m = __start_BUS_ERROR_MAP;
while (m < __stop_BUS_ERROR_MAP) {
/* For magic ELF error maps, the end marker might
* appear in the middle of things, since multiple maps
* might appear in the same section. Hence, let's skip
- * over it, but realign the pointer to the netx 8byte
+ * over it, but realign the pointer to the next 8 byte
* boundary, which is the selected alignment for the
* arrays. */
if (m->code == BUS_ERROR_MAP_END_MARKER) {
@@ -256,25 +256,24 @@ int bus_error_setfv(sd_bus_error *e, const char *name, const char *format, va_li
if (!name)
return 0;
- if (!e)
- goto finish;
- assert_return(!bus_error_is_dirty(e), -EINVAL);
+ if (e) {
+ assert_return(!bus_error_is_dirty(e), -EINVAL);
- e->name = strdup(name);
- if (!e->name) {
- *e = BUS_ERROR_OOM;
- return -ENOMEM;
- }
+ e->name = strdup(name);
+ if (!e->name) {
+ *e = BUS_ERROR_OOM;
+ return -ENOMEM;
+ }
- /* If we hit OOM on formatting the pretty message, we ignore
- * this, since we at least managed to write the error name */
- if (format)
- (void) vasprintf((char**) &e->message, format, ap);
+ /* If we hit OOM on formatting the pretty message, we ignore
+ * this, since we at least managed to write the error name */
+ if (format)
+ (void) vasprintf((char**) &e->message, format, ap);
- e->_need_free = 1;
+ e->_need_free = 1;
+ }
-finish:
return -bus_error_name_to_errno(name);
}
@@ -565,7 +564,7 @@ _public_ int sd_bus_error_set_errnof(sd_bus_error *e, int error, const char *for
const char *bus_error_message(const sd_bus_error *e, int error) {
if (e) {
- /* Sometimes the D-Bus server is a little bit too verbose with
+ /* Sometimes, the D-Bus server is a little bit too verbose with
* its error messages, so let's override them here */
if (sd_bus_error_has_name(e, SD_BUS_ERROR_ACCESS_DENIED))
return "Access denied";
@@ -580,27 +579,29 @@ const char *bus_error_message(const sd_bus_error *e, int error) {
return strerror(error);
}
+static bool map_ok(const sd_bus_error_map *map) {
+ for (; map->code != BUS_ERROR_MAP_END_MARKER; map++)
+ if (!map->name || map->code <=0)
+ return false;
+ return true;
+}
+
_public_ int sd_bus_error_add_map(const sd_bus_error_map *map) {
const sd_bus_error_map **maps = NULL;
unsigned n = 0;
assert_return(map, -EINVAL);
+ assert_return(map_ok(map), -EINVAL);
- if (additional_error_maps) {
- for (;; n++) {
- if (additional_error_maps[n] == NULL)
- break;
-
+ if (additional_error_maps)
+ for (; additional_error_maps[n] != NULL; n++)
if (additional_error_maps[n] == map)
return 0;
- }
- }
maps = realloc_multiply(additional_error_maps, sizeof(struct sd_bus_error_map*), n + 2);
if (!maps)
return -ENOMEM;
-
maps[n] = map;
maps[n+1] = NULL;
diff --git a/src/libsystemd/sd-bus/bus-error.h b/src/libsystemd/sd-bus/bus-error.h
index fb0199c948..d7fd8612d0 100644
--- a/src/libsystemd/sd-bus/bus-error.h
+++ b/src/libsystemd/sd-bus/bus-error.h
@@ -24,6 +24,7 @@
#include <stdbool.h>
#include "sd-bus.h"
+
#include "macro.h"
bool bus_error_is_dirty(sd_bus_error *e);
diff --git a/src/libsystemd/sd-bus/bus-gvariant.c b/src/libsystemd/sd-bus/bus-gvariant.c
index 402d43d66d..ec027590b2 100644
--- a/src/libsystemd/sd-bus/bus-gvariant.c
+++ b/src/libsystemd/sd-bus/bus-gvariant.c
@@ -19,9 +19,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "bus-type.h"
#include "bus-gvariant.h"
#include "bus-signature.h"
+#include "bus-type.h"
int bus_gvariant_get_size(const char *signature) {
const char *p;
diff --git a/src/libsystemd/sd-bus/bus-internal.c b/src/libsystemd/sd-bus/bus-internal.c
index fea796cd30..d9f9cd1c5e 100644
--- a/src/libsystemd/sd-bus/bus-internal.c
+++ b/src/libsystemd/sd-bus/bus-internal.c
@@ -19,8 +19,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "bus-message.h"
+#include "alloc-util.h"
#include "bus-internal.h"
+#include "bus-message.h"
+#include "hexdecoct.h"
+#include "string-util.h"
bool object_path_is_valid(const char *p) {
const char *q;
diff --git a/src/libsystemd/sd-bus/bus-internal.h b/src/libsystemd/sd-bus/bus-internal.h
index e399701beb..35aed634f7 100644
--- a/src/libsystemd/sd-bus/bus-internal.h
+++ b/src/libsystemd/sd-bus/bus-internal.h
@@ -21,21 +21,21 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/socket.h>
#include <pthread.h>
-
-#include "hashmap.h"
-#include "prioq.h"
-#include "list.h"
-#include "util.h"
-#include "refcnt.h"
-#include "socket-util.h"
+#include <sys/socket.h>
#include "sd-bus.h"
+
#include "bus-error.h"
-#include "bus-match.h"
#include "bus-kernel.h"
+#include "bus-match.h"
+#include "hashmap.h"
#include "kdbus.h"
+#include "list.h"
+#include "prioq.h"
+#include "refcnt.h"
+#include "socket-util.h"
+#include "util.h"
struct reply_callback {
sd_bus_message_handler_t callback;
@@ -381,7 +381,7 @@ char *bus_address_escape(const char *v);
* bus from the callback doesn't destroy the object we are working
* on */
#define BUS_DONT_DESTROY(bus) \
- _cleanup_bus_unref_ _unused_ sd_bus *_dont_destroy_##bus = sd_bus_ref(bus)
+ _cleanup_(sd_bus_unrefp) _unused_ sd_bus *_dont_destroy_##bus = sd_bus_ref(bus)
int bus_set_address_system(sd_bus *bus);
int bus_set_address_user(sd_bus *bus);
diff --git a/src/libsystemd/sd-bus/bus-introspect.c b/src/libsystemd/sd-bus/bus-introspect.c
index 3149a56397..a90536bac9 100644
--- a/src/libsystemd/sd-bus/bus-introspect.c
+++ b/src/libsystemd/sd-bus/bus-introspect.c
@@ -19,11 +19,14 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
-#include "bus-introspect.h"
-#include "bus-signature.h"
#include "bus-internal.h"
+#include "bus-introspect.h"
#include "bus-protocol.h"
+#include "bus-signature.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "string-util.h"
+#include "util.h"
int introspect_begin(struct introspect *i, bool trusted) {
assert(i);
diff --git a/src/libsystemd/sd-bus/bus-introspect.h b/src/libsystemd/sd-bus/bus-introspect.h
index 1914e6cb8b..57c2430ee8 100644
--- a/src/libsystemd/sd-bus/bus-introspect.h
+++ b/src/libsystemd/sd-bus/bus-introspect.h
@@ -24,6 +24,7 @@
#include <stdio.h>
#include "sd-bus.h"
+
#include "set.h"
struct introspect {
diff --git a/src/libsystemd/sd-bus/bus-kernel.c b/src/libsystemd/sd-bus/bus-kernel.c
index 577a8b44c3..e7d6170eec 100644
--- a/src/libsystemd/sd-bus/bus-kernel.c
+++ b/src/libsystemd/sd-bus/bus-kernel.c
@@ -34,19 +34,24 @@
#include <libgen.h>
#undef basename
-#include "util.h"
-#include "strv.h"
-#include "memfd-util.h"
-#include "capability.h"
-#include "fileio.h"
-#include "formats-util.h"
-
+#include "alloc-util.h"
+#include "bus-bloom.h"
#include "bus-internal.h"
-#include "bus-message.h"
#include "bus-kernel.h"
-#include "bus-bloom.h"
-#include "bus-util.h"
#include "bus-label.h"
+#include "bus-message.h"
+#include "bus-util.h"
+#include "capability-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "formats-util.h"
+#include "memfd-util.h"
+#include "parse-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "user-util.h"
+#include "util.h"
#define UNIQUE_NAME_MAX (3+DECIMAL_STR_MAX(uint64_t))
@@ -265,8 +270,8 @@ static int bus_message_setup_kmsg(sd_bus *b, sd_bus_message *m) {
struct bus_body_part *part;
struct kdbus_item *d;
const char *destination;
- bool well_known;
- uint64_t unique;
+ bool well_known = false;
+ uint64_t dst_id;
size_t sz, dl;
unsigned i;
int r;
@@ -283,13 +288,21 @@ static int bus_message_setup_kmsg(sd_bus *b, sd_bus_message *m) {
destination = m->destination ?: m->destination_ptr;
if (destination) {
- r = bus_kernel_parse_unique_name(destination, &unique);
+ r = bus_kernel_parse_unique_name(destination, &dst_id);
if (r < 0)
return r;
-
- well_known = r == 0;
+ if (r == 0) {
+ well_known = true;
+
+ /* verify_destination_id will usually be 0, which makes the kernel
+ * driver only look at the provided well-known name. Otherwise,
+ * the kernel will make sure the provided destination id matches
+ * the owner of the provided well-known-name, and fail if they
+ * differ. Currently, this is only needed for bus-proxyd. */
+ dst_id = m->verify_destination_id;
+ }
} else
- well_known = false;
+ dst_id = KDBUS_DST_ID_BROADCAST;
sz = offsetof(struct kdbus_msg, items);
@@ -327,15 +340,7 @@ static int bus_message_setup_kmsg(sd_bus *b, sd_bus_message *m) {
((m->header->flags & BUS_MESSAGE_NO_AUTO_START) ? KDBUS_MSG_NO_AUTO_START : 0) |
((m->header->type == SD_BUS_MESSAGE_SIGNAL) ? KDBUS_MSG_SIGNAL : 0);
- if (well_known)
- /* verify_destination_id will usually be 0, which makes the kernel driver only look
- * at the provided well-known name. Otherwise, the kernel will make sure the provided
- * destination id matches the owner of the provided weel-known-name, and fail if they
- * differ. Currently, this is only needed for bus-proxyd. */
- m->kdbus->dst_id = m->verify_destination_id;
- else
- m->kdbus->dst_id = destination ? unique : KDBUS_DST_ID_BROADCAST;
-
+ m->kdbus->dst_id = dst_id;
m->kdbus->payload_type = KDBUS_PAYLOAD_DBUS;
m->kdbus->cookie = m->header->dbus2.cookie;
m->kdbus->priority = m->priority;
@@ -845,7 +850,8 @@ static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k) {
if (k->src_id == KDBUS_SRC_ID_KERNEL)
bus_message_set_sender_driver(bus, m);
else {
- snprintf(m->sender_buffer, sizeof(m->sender_buffer), ":1.%llu", (unsigned long long) k->src_id);
+ xsprintf(m->sender_buffer, ":1.%llu",
+ (unsigned long long)k->src_id);
m->sender = m->creds.unique_name = m->sender_buffer;
}
@@ -856,7 +862,8 @@ static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k) {
else if (k->dst_id == KDBUS_DST_ID_NAME)
m->destination = bus->unique_name; /* fill in unique name if the well-known name is missing */
else {
- snprintf(m->destination_buffer, sizeof(m->destination_buffer), ":1.%llu", (unsigned long long) k->dst_id);
+ xsprintf(m->destination_buffer, ":1.%llu",
+ (unsigned long long)k->dst_id);
m->destination = m->destination_buffer;
}
@@ -1138,7 +1145,7 @@ int bus_kernel_write_message(sd_bus *bus, sd_bus_message *m, bool hint_sync_call
r = ioctl(bus->output_fd, KDBUS_CMD_SEND, &cmd);
if (r < 0) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus_message *reply;
if (errno == EAGAIN || errno == EINTR)
@@ -1217,7 +1224,7 @@ static int push_name_owner_changed(
const char *new_owner,
const struct kdbus_timestamp *ts) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
int r;
assert(bus);
@@ -1304,7 +1311,7 @@ static int translate_reply(
const struct kdbus_item *d,
const struct kdbus_timestamp *ts) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
int r;
assert(bus);
@@ -1433,12 +1440,12 @@ int bus_kernel_pop_memfd(sd_bus *bus, void **address, size_t *mapped, size_t *al
if (!bus || !bus->is_kernel)
return -EOPNOTSUPP;
- assert_se(pthread_mutex_lock(&bus->memfd_cache_mutex) >= 0);
+ assert_se(pthread_mutex_lock(&bus->memfd_cache_mutex) == 0);
if (bus->n_memfd_cache <= 0) {
int r;
- assert_se(pthread_mutex_unlock(&bus->memfd_cache_mutex) >= 0);
+ assert_se(pthread_mutex_unlock(&bus->memfd_cache_mutex) == 0);
r = memfd_new(bus->description);
if (r < 0)
@@ -1460,7 +1467,7 @@ int bus_kernel_pop_memfd(sd_bus *bus, void **address, size_t *mapped, size_t *al
*allocated = c->allocated;
fd = c->fd;
- assert_se(pthread_mutex_unlock(&bus->memfd_cache_mutex) >= 0);
+ assert_se(pthread_mutex_unlock(&bus->memfd_cache_mutex) == 0);
return fd;
}
@@ -1484,10 +1491,10 @@ void bus_kernel_push_memfd(sd_bus *bus, int fd, void *address, size_t mapped, si
return;
}
- assert_se(pthread_mutex_lock(&bus->memfd_cache_mutex) >= 0);
+ assert_se(pthread_mutex_lock(&bus->memfd_cache_mutex) == 0);
if (bus->n_memfd_cache >= ELEMENTSOF(bus->memfd_cache)) {
- assert_se(pthread_mutex_unlock(&bus->memfd_cache_mutex) >= 0);
+ assert_se(pthread_mutex_unlock(&bus->memfd_cache_mutex) == 0);
close_and_munmap(fd, address, mapped);
return;
@@ -1507,7 +1514,7 @@ void bus_kernel_push_memfd(sd_bus *bus, int fd, void *address, size_t mapped, si
c->allocated = allocated;
}
- assert_se(pthread_mutex_unlock(&bus->memfd_cache_mutex) >= 0);
+ assert_se(pthread_mutex_unlock(&bus->memfd_cache_mutex) == 0);
}
void bus_kernel_flush_memfd(sd_bus *b) {
diff --git a/src/libsystemd/sd-bus/bus-match.c b/src/libsystemd/sd-bus/bus-match.c
index 7234e7926a..cb3c8bbd26 100644
--- a/src/libsystemd/sd-bus/bus-match.c
+++ b/src/libsystemd/sd-bus/bus-match.c
@@ -19,10 +19,15 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "alloc-util.h"
#include "bus-internal.h"
-#include "bus-message.h"
#include "bus-match.h"
+#include "bus-message.h"
#include "bus-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "hexdecoct.h"
+#include "string-util.h"
#include "strv.h"
/* Example:
@@ -312,7 +317,7 @@ int bus_match_run(
/* Run the callback. And then invoke siblings. */
if (node->leaf.callback->callback) {
- _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
sd_bus_slot *slot;
slot = container_of(node->leaf.callback, sd_bus_slot, match_callback);
diff --git a/src/libsystemd/sd-bus/bus-match.h b/src/libsystemd/sd-bus/bus-match.h
index 53ee0463ca..bc85af3ec0 100644
--- a/src/libsystemd/sd-bus/bus-match.h
+++ b/src/libsystemd/sd-bus/bus-match.h
@@ -21,10 +21,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "hashmap.h"
-
#include "sd-bus.h"
+#include "hashmap.h"
+
enum bus_match_node_type {
BUS_MATCH_ROOT,
BUS_MATCH_VALUE,
diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c
index 72e2b9f785..e939359338 100644
--- a/src/libsystemd/sd-bus/bus-message.c
+++ b/src/libsystemd/sd-bus/bus-message.c
@@ -23,19 +23,23 @@
#include <fcntl.h>
#include <sys/mman.h>
-#include "util.h"
-#include "utf8.h"
-#include "strv.h"
-#include "time-util.h"
-#include "memfd-util.h"
-
#include "sd-bus.h"
-#include "bus-message.h"
+
+#include "alloc-util.h"
+#include "bus-gvariant.h"
#include "bus-internal.h"
-#include "bus-type.h"
+#include "bus-message.h"
#include "bus-signature.h"
-#include "bus-gvariant.h"
+#include "bus-type.h"
#include "bus-util.h"
+#include "fd-util.h"
+#include "io-util.h"
+#include "memfd-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "time-util.h"
+#include "utf8.h"
+#include "util.h"
static int message_append_basic(sd_bus_message *m, char type, const void *p, const void **stored);
@@ -798,7 +802,7 @@ _public_ int sd_bus_message_new_method_errorf(
const char *format,
...) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
va_list ap;
assert_return(name, -EINVAL);
@@ -817,7 +821,7 @@ _public_ int sd_bus_message_new_method_errno(
int error,
const sd_bus_error *p) {
- _cleanup_bus_error_free_ sd_bus_error berror = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error berror = SD_BUS_ERROR_NULL;
if (sd_bus_error_is_set(p))
return sd_bus_message_new_method_error(call, m, p);
@@ -834,7 +838,7 @@ _public_ int sd_bus_message_new_method_errnof(
const char *format,
...) {
- _cleanup_bus_error_free_ sd_bus_error berror = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error berror = SD_BUS_ERROR_NULL;
va_list ap;
va_start(ap, format);
@@ -915,7 +919,9 @@ fail:
}
_public_ sd_bus_message* sd_bus_message_ref(sd_bus_message *m) {
- assert_return(m, NULL);
+
+ if (!m)
+ return NULL;
assert(m->n_ref > 0);
m->n_ref++;
@@ -5834,7 +5840,7 @@ _public_ sd_bus *sd_bus_message_get_bus(sd_bus_message *m) {
}
int bus_message_remarshal(sd_bus *bus, sd_bus_message **m) {
- _cleanup_bus_message_unref_ sd_bus_message *n = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *n = NULL;
usec_t timeout;
int r;
diff --git a/src/libsystemd/sd-bus/bus-message.h b/src/libsystemd/sd-bus/bus-message.h
index ff25003461..4c91dbae09 100644
--- a/src/libsystemd/sd-bus/bus-message.h
+++ b/src/libsystemd/sd-bus/bus-message.h
@@ -21,15 +21,16 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdbool.h>
#include <byteswap.h>
+#include <stdbool.h>
#include <sys/socket.h>
-#include "macro.h"
#include "sd-bus.h"
-#include "time-util.h"
+
#include "bus-creds.h"
#include "bus-protocol.h"
+#include "macro.h"
+#include "time-util.h"
struct bus_container {
char enclosing;
diff --git a/src/libsystemd/sd-bus/bus-objects.c b/src/libsystemd/sd-bus/bus-objects.c
index 1d061cb9cf..4fc5f8dfa3 100644
--- a/src/libsystemd/sd-bus/bus-objects.c
+++ b/src/libsystemd/sd-bus/bus-objects.c
@@ -19,16 +19,18 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "strv.h"
-#include "set.h"
+#include "alloc-util.h"
#include "bus-internal.h"
+#include "bus-introspect.h"
#include "bus-message.h"
-#include "bus-type.h"
+#include "bus-objects.h"
#include "bus-signature.h"
-#include "bus-introspect.h"
-#include "bus-util.h"
#include "bus-slot.h"
-#include "bus-objects.h"
+#include "bus-type.h"
+#include "bus-util.h"
+#include "set.h"
+#include "string-util.h"
+#include "strv.h"
static int node_vtable_get_userdata(
sd_bus *bus,
@@ -270,7 +272,7 @@ static int node_callbacks_run(
assert(found_object);
LIST_FOREACH(callbacks, c, first) {
- _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
sd_bus_slot *slot;
if (bus->nodes_modified)
@@ -355,7 +357,7 @@ static int method_callbacks_run(
bool require_fallback,
bool *found_object) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
const char *signature;
void *u;
int r;
@@ -578,8 +580,8 @@ static int property_get_set_callbacks_run(
bool is_get,
bool *found_object) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
sd_bus_slot *slot;
void *u = NULL;
int r;
@@ -779,7 +781,7 @@ static int property_get_all_callbacks_run(
const char *iface,
bool *found_object) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
struct node_vtable *c;
bool found_interface;
int r;
@@ -802,7 +804,7 @@ static int property_get_all_callbacks_run(
streq(iface, "org.freedesktop.DBus.Introspectable");
LIST_FOREACH(vtables, c, first) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
void *u;
if (require_fallback && !c->is_fallback)
@@ -879,7 +881,7 @@ static int bus_node_exists(
}
LIST_FOREACH(vtables, c, n->vtables) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
if (require_fallback && !c->is_fallback)
continue;
@@ -901,8 +903,8 @@ static int process_introspect(
bool require_fallback,
bool *found_object) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_set_free_free_ Set *s = NULL;
const char *previous_interface = NULL;
struct introspect intro;
@@ -1162,8 +1164,8 @@ static int process_get_managed_objects(
bool require_fallback,
bool *found_object) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_set_free_free_ Set *s = NULL;
Iterator i;
char *path;
@@ -1578,25 +1580,14 @@ _public_ int sd_bus_add_fallback(
return bus_add_object(bus, slot, true, prefix, callback, userdata);
}
-static unsigned long vtable_member_hash_func(const void *a, const uint8_t hash_key[HASH_KEY_SIZE]) {
+static void vtable_member_hash_func(const void *a, struct siphash *state) {
const struct vtable_member *m = a;
- uint8_t hash_key2[HASH_KEY_SIZE];
- unsigned long ret;
assert(m);
- ret = string_hash_func(m->path, hash_key);
-
- /* Use a slightly different hash key for the interface */
- memcpy(hash_key2, hash_key, HASH_KEY_SIZE);
- hash_key2[0]++;
- ret ^= string_hash_func(m->interface, hash_key2);
-
- /* And an even different one for the member */
- hash_key2[0]++;
- ret ^= string_hash_func(m->member, hash_key2);
-
- return ret;
+ string_hash_func(m->path, state);
+ string_hash_func(m->interface, state);
+ string_hash_func(m->member, state);
}
static int vtable_member_compare_func(const void *a, const void *b) {
@@ -1890,8 +1881,8 @@ static int emit_properties_changed_on_interface(
bool *found_interface,
char **names) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
bool has_invalidating = false, has_changing = false;
struct vtable_member key = {};
struct node_vtable *c;
@@ -2185,7 +2176,7 @@ static int object_added_append_all_prefix(
return 0;
LIST_FOREACH(vtables, c, n->vtables) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
void *u = NULL;
if (require_fallback && !c->is_fallback)
@@ -2314,7 +2305,7 @@ static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *p
_public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) {
BUS_DONT_DESTROY(bus);
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
struct node *object_manager;
int r;
@@ -2398,7 +2389,7 @@ static int object_removed_append_all_prefix(
return 0;
LIST_FOREACH(vtables, c, n->vtables) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
void *u = NULL;
if (require_fallback && !c->is_fallback)
@@ -2484,7 +2475,7 @@ static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char
_public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) {
BUS_DONT_DESTROY(bus);
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
struct node *object_manager;
int r;
@@ -2552,7 +2543,7 @@ static int interfaces_added_append_one_prefix(
const char *interface,
bool require_fallback) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
bool found_interface = false;
struct node_vtable *c;
struct node *n;
@@ -2647,7 +2638,7 @@ static int interfaces_added_append_one(
_public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
BUS_DONT_DESTROY(bus);
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
struct node *object_manager;
char **i;
int r;
@@ -2731,7 +2722,7 @@ _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const c
}
_public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
struct node *object_manager;
int r;
diff --git a/src/libsystemd/sd-bus/bus-slot.c b/src/libsystemd/sd-bus/bus-slot.c
index b149ea16da..e396d6c3b4 100644
--- a/src/libsystemd/sd-bus/bus-slot.c
+++ b/src/libsystemd/sd-bus/bus-slot.c
@@ -20,9 +20,12 @@
***/
#include "sd-bus.h"
+
+#include "alloc-util.h"
#include "bus-control.h"
#include "bus-objects.h"
#include "bus-slot.h"
+#include "string-util.h"
sd_bus_slot *bus_slot_allocate(
sd_bus *bus,
@@ -54,7 +57,9 @@ sd_bus_slot *bus_slot_allocate(
}
_public_ sd_bus_slot* sd_bus_slot_ref(sd_bus_slot *slot) {
- assert_return(slot, NULL);
+
+ if (!slot)
+ return NULL;
assert(slot->n_ref > 0);
diff --git a/src/libsystemd/sd-bus/bus-slot.h b/src/libsystemd/sd-bus/bus-slot.h
index 23a15e4d02..c997e58f9a 100644
--- a/src/libsystemd/sd-bus/bus-slot.h
+++ b/src/libsystemd/sd-bus/bus-slot.h
@@ -22,6 +22,7 @@
***/
#include "sd-bus.h"
+
#include "bus-internal.h"
sd_bus_slot *bus_slot_allocate(sd_bus *bus, bool floating, BusSlotType type, size_t extra, void *userdata);
diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c
index d0b1e3d7dc..1df571ac92 100644
--- a/src/libsystemd/sd-bus/bus-socket.c
+++ b/src/libsystemd/sd-bus/bus-socket.c
@@ -20,22 +20,29 @@
***/
#include <endian.h>
+#include <poll.h>
#include <stdlib.h>
#include <unistd.h>
-#include <poll.h>
+#include "sd-bus.h"
#include "sd-daemon.h"
-#include "util.h"
-#include "macro.h"
-#include "missing.h"
-#include "utf8.h"
-#include "formats-util.h"
-#include "signal-util.h"
-#include "sd-bus.h"
-#include "bus-socket.h"
+#include "alloc-util.h"
#include "bus-internal.h"
#include "bus-message.h"
+#include "bus-socket.h"
+#include "fd-util.h"
+#include "formats-util.h"
+#include "hexdecoct.h"
+#include "macro.h"
+#include "missing.h"
+#include "selinux-util.h"
+#include "signal-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
+#include "user-util.h"
+#include "utf8.h"
+#include "util.h"
#define SNDBUF_SIZE (8*1024*1024)
@@ -602,9 +609,11 @@ static void bus_get_peercred(sd_bus *b) {
b->ucred_valid = getpeercred(b->input_fd, &b->ucred) >= 0;
/* Get the SELinux context of the peer */
- r = getpeersec(b->input_fd, &b->label);
- if (r < 0 && r != -EOPNOTSUPP)
- log_debug_errno(r, "Failed to determine peer security context: %m");
+ if (mac_selinux_have()) {
+ r = getpeersec(b->input_fd, &b->label);
+ if (r < 0 && r != -EOPNOTSUPP)
+ log_debug_errno(r, "Failed to determine peer security context: %m");
+ }
}
static int bus_socket_start_auth_client(sd_bus *b) {
diff --git a/src/libsystemd/sd-bus/bus-track.c b/src/libsystemd/sd-bus/bus-track.c
index e43891be25..2f6d7e9452 100644
--- a/src/libsystemd/sd-bus/bus-track.c
+++ b/src/libsystemd/sd-bus/bus-track.c
@@ -20,9 +20,11 @@
***/
#include "sd-bus.h"
-#include "bus-util.h"
+
+#include "alloc-util.h"
#include "bus-internal.h"
#include "bus-track.h"
+#include "bus-util.h"
struct sd_bus_track {
unsigned n_ref;
@@ -109,7 +111,9 @@ _public_ int sd_bus_track_new(
}
_public_ sd_bus_track* sd_bus_track_ref(sd_bus_track *track) {
- assert_return(track, NULL);
+
+ if (!track)
+ return NULL;
assert(track->n_ref > 0);
@@ -159,7 +163,7 @@ static int on_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus
}
_public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) {
- _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
+ _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
_cleanup_free_ char *n = NULL;
const char *match;
int r;
@@ -205,7 +209,7 @@ _public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) {
}
_public_ int sd_bus_track_remove_name(sd_bus_track *track, const char *name) {
- _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
+ _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
_cleanup_free_ char *n = NULL;
assert_return(name, -EINVAL);
diff --git a/src/libsystemd/sd-bus/bus-type.h b/src/libsystemd/sd-bus/bus-type.h
index 581574ab73..ad89e6c911 100644
--- a/src/libsystemd/sd-bus/bus-type.h
+++ b/src/libsystemd/sd-bus/bus-type.h
@@ -23,9 +23,10 @@
#include <stdbool.h>
-#include "macro.h"
#include "sd-bus.h"
+#include "macro.h"
+
bool bus_type_is_valid(char c) _const_;
bool bus_type_is_valid_in_signature(char c) _const_;
bool bus_type_is_basic(char c) _const_;
diff --git a/src/libsystemd/sd-bus/busctl-introspect.c b/src/libsystemd/sd-bus/busctl-introspect.c
index abe482fc46..71f962b00c 100644
--- a/src/libsystemd/sd-bus/busctl-introspect.c
+++ b/src/libsystemd/sd-bus/busctl-introspect.c
@@ -19,11 +19,13 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
-#include "xml.h"
-#include "sd-bus-vtable.h"
+#include "sd-bus.h"
+#include "alloc-util.h"
#include "busctl-introspect.h"
+#include "string-util.h"
+#include "util.h"
+#include "xml.h"
#define NODE_DEPTH_MAX 16
diff --git a/src/libsystemd/sd-bus/busctl.c b/src/libsystemd/sd-bus/busctl.c
index 5d07d5809c..3e66589489 100644
--- a/src/libsystemd/sd-bus/busctl.c
+++ b/src/libsystemd/sd-bus/busctl.c
@@ -21,22 +21,27 @@
#include <getopt.h>
-#include "strv.h"
-#include "util.h"
-#include "log.h"
-#include "build.h"
-#include "pager.h"
-#include "path-util.h"
-#include "set.h"
-
#include "sd-bus.h"
-#include "bus-internal.h"
-#include "bus-util.h"
+
+#include "alloc-util.h"
#include "bus-dump.h"
+#include "bus-internal.h"
#include "bus-signature.h"
#include "bus-type.h"
+#include "bus-util.h"
#include "busctl-introspect.h"
+#include "escape.h"
+#include "fd-util.h"
+#include "locale-util.h"
+#include "log.h"
+#include "pager.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "set.h"
+#include "strv.h"
#include "terminal-util.h"
+#include "user-util.h"
+#include "util.h"
static bool arg_no_pager = false;
static bool arg_legend = true;
@@ -132,7 +137,7 @@ static int list_bus_names(sd_bus *bus, char **argv) {
}
STRV_FOREACH(i, merged) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
sd_id128_t mid;
if (hashmap_get(names, *i) == NAME_IS_ACTIVATABLE) {
@@ -329,8 +334,8 @@ static int find_nodes(sd_bus *bus, const char *service, const char *path, Set *p
.on_path = on_path,
};
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
const char *xml;
int r;
@@ -629,22 +634,24 @@ typedef struct Member {
uint64_t flags;
} Member;
-static unsigned long member_hash_func(const void *p, const uint8_t hash_key[]) {
+static void member_hash_func(const void *p, struct siphash *state) {
const Member *m = p;
- unsigned long ul;
+ uint64_t arity = 1;
assert(m);
assert(m->type);
- ul = string_hash_func(m->type, hash_key);
+ string_hash_func(m->type, state);
+
+ arity += !!m->name + !!m->interface;
+
+ uint64_hash_func(&arity, state);
if (m->name)
- ul ^= string_hash_func(m->name, hash_key);
+ string_hash_func(m->name, state);
if (m->interface)
- ul ^= string_hash_func(m->interface, hash_key);
-
- return ul;
+ string_hash_func(m->interface, state);
}
static int member_compare_func(const void *a, const void *b) {
@@ -860,8 +867,8 @@ static int introspect(sd_bus *bus, char **argv) {
.on_property = on_property,
};
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(member_set_freep) Set *members = NULL;
Iterator i;
Member *m;
@@ -1125,7 +1132,7 @@ static int monitor(sd_bus *bus, char *argv[], int (*dump)(sd_bus_message *m, FIL
log_info("Monitoring bus message stream.");
for (;;) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
r = sd_bus_process(bus, &m);
if (r < 0)
@@ -1175,7 +1182,7 @@ static int capture(sd_bus *bus, char *argv[]) {
}
static int status(sd_bus *bus, char *argv[]) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
pid_t pid;
int r;
@@ -1482,8 +1489,8 @@ static int message_append_cmdline(sd_bus_message *m, const char *signature, char
}
static int call(sd_bus *bus, 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_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
int r;
assert(bus);
@@ -1569,7 +1576,7 @@ static int call(sd_bus *bus, char *argv[]) {
}
static int get_property(sd_bus *bus, char *argv[]) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
unsigned n;
char **i;
int r;
@@ -1583,7 +1590,7 @@ static int get_property(sd_bus *bus, char *argv[]) {
}
STRV_FOREACH(i, argv + 4) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
const char *contents = NULL;
char type;
@@ -1627,8 +1634,8 @@ static int get_property(sd_bus *bus, char *argv[]) {
}
static int set_property(sd_bus *bus, char *argv[]) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
unsigned n;
char **p;
int r;
@@ -1786,9 +1793,7 @@ static int parse_argv(int argc, char *argv[]) {
return help();
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case ARG_NO_PAGER:
arg_no_pager = true;
@@ -1971,7 +1976,7 @@ static int busctl_main(sd_bus *bus, int argc, char *argv[]) {
}
int main(int argc, char *argv[]) {
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
log_parse_environment();
diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c
index 53d1c6f61d..3c2232a354 100644
--- a/src/libsystemd/sd-bus/sd-bus.c
+++ b/src/libsystemd/sd-bus/sd-bus.c
@@ -20,35 +20,40 @@
***/
#include <endian.h>
-#include <stdlib.h>
-#include <unistd.h>
#include <netdb.h>
#include <poll.h>
-#include <sys/mman.h>
#include <pthread.h>
-
-#include "util.h"
-#include "macro.h"
-#include "strv.h"
-#include "missing.h"
-#include "def.h"
-#include "cgroup-util.h"
-#include "hostname-util.h"
-#include "bus-label.h"
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <unistd.h>
#include "sd-bus.h"
+
+#include "alloc-util.h"
+#include "bus-container.h"
+#include "bus-control.h"
#include "bus-internal.h"
-#include "bus-message.h"
-#include "bus-type.h"
-#include "bus-socket.h"
#include "bus-kernel.h"
-#include "bus-control.h"
+#include "bus-label.h"
+#include "bus-message.h"
#include "bus-objects.h"
-#include "bus-util.h"
-#include "bus-container.h"
#include "bus-protocol.h"
-#include "bus-track.h"
#include "bus-slot.h"
+#include "bus-socket.h"
+#include "bus-track.h"
+#include "bus-type.h"
+#include "bus-util.h"
+#include "cgroup-util.h"
+#include "def.h"
+#include "fd-util.h"
+#include "hexdecoct.h"
+#include "hostname-util.h"
+#include "macro.h"
+#include "missing.h"
+#include "parse-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "util.h"
#define log_debug_bus_message(m) \
do { \
@@ -414,7 +419,7 @@ static int hello_callback(sd_bus_message *reply, void *userdata, sd_bus_error *e
}
static int bus_send_hello(sd_bus *bus) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
int r;
assert(bus);
@@ -1475,7 +1480,9 @@ static void bus_enter_closing(sd_bus *bus) {
}
_public_ sd_bus *sd_bus_ref(sd_bus *bus) {
- assert_return(bus, NULL);
+
+ if (!bus)
+ return NULL;
assert_se(REFCNT_INC(bus->n_ref) >= 2);
@@ -1729,7 +1736,7 @@ static int dispatch_rqueue(sd_bus *bus, bool hint_priority, int64_t priority, sd
}
static int bus_send_internal(sd_bus *bus, sd_bus_message *_m, uint64_t *cookie, bool hint_sync_call) {
- _cleanup_bus_message_unref_ sd_bus_message *m = sd_bus_message_ref(_m);
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = sd_bus_message_ref(_m);
int r;
assert_return(m, -EINVAL);
@@ -1877,8 +1884,8 @@ _public_ int sd_bus_call_async(
void *userdata,
uint64_t usec) {
- _cleanup_bus_message_unref_ sd_bus_message *m = sd_bus_message_ref(_m);
- _cleanup_bus_slot_unref_ sd_bus_slot *s = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = sd_bus_message_ref(_m);
+ _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *s = NULL;
int r;
assert_return(m, -EINVAL);
@@ -1976,7 +1983,7 @@ _public_ int sd_bus_call(
sd_bus_error *error,
sd_bus_message **reply) {
- _cleanup_bus_message_unref_ sd_bus_message *m = sd_bus_message_ref(_m);
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = sd_bus_message_ref(_m);
usec_t timeout;
uint64_t cookie;
unsigned i;
@@ -2215,8 +2222,8 @@ _public_ int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec) {
}
static int process_timeout(sd_bus *bus) {
- _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message* m = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message* m = NULL;
struct reply_callback *c;
sd_bus_slot *slot;
usec_t n;
@@ -2297,8 +2304,8 @@ static int process_hello(sd_bus *bus, sd_bus_message *m) {
}
static int process_reply(sd_bus *bus, sd_bus_message *m) {
- _cleanup_bus_message_unref_ sd_bus_message *synthetic_reply = NULL;
- _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *synthetic_reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
struct reply_callback *c;
sd_bus_slot *slot;
int r;
@@ -2377,7 +2384,7 @@ static int process_reply(sd_bus *bus, sd_bus_message *m) {
}
static int process_filter(sd_bus *bus, sd_bus_message *m) {
- _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
struct filter_callback *l;
int r;
@@ -2443,7 +2450,7 @@ static int process_match(sd_bus *bus, sd_bus_message *m) {
}
static int process_builtin(sd_bus *bus, sd_bus_message *m) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
int r;
assert(bus);
@@ -2575,7 +2582,7 @@ static int dispatch_track(sd_bus *bus) {
}
static int process_running(sd_bus *bus, bool hint_priority, int64_t priority, sd_bus_message **ret) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
int r;
assert(bus);
@@ -2639,7 +2646,7 @@ null_message:
}
static int process_closing(sd_bus *bus, sd_bus_message **ret) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
struct reply_callback *c;
int r;
@@ -2648,7 +2655,7 @@ static int process_closing(sd_bus *bus, sd_bus_message **ret) {
c = ordered_hashmap_first(bus->reply_callbacks);
if (c) {
- _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
sd_bus_slot *slot;
/* First, fail all outstanding method calls */
@@ -3454,6 +3461,171 @@ _public_ int sd_bus_path_decode(const char *path, const char *prefix, char **ext
return 1;
}
+_public_ int sd_bus_path_encode_many(char **out, const char *path_template, ...) {
+ _cleanup_strv_free_ char **labels = NULL;
+ char *path, *path_pos, **label_pos;
+ const char *sep, *template_pos;
+ size_t path_length;
+ va_list list;
+ int r;
+
+ assert_return(out, -EINVAL);
+ assert_return(path_template, -EINVAL);
+
+ path_length = strlen(path_template);
+
+ va_start(list, path_template);
+ for (sep = strchr(path_template, '%'); sep; sep = strchr(sep + 1, '%')) {
+ const char *arg;
+ char *label;
+
+ arg = va_arg(list, const char *);
+ if (!arg) {
+ va_end(list);
+ return -EINVAL;
+ }
+
+ label = bus_label_escape(arg);
+ if (!label) {
+ va_end(list);
+ return -ENOMEM;
+ }
+
+ r = strv_consume(&labels, label);
+ if (r < 0) {
+ va_end(list);
+ return r;
+ }
+
+ /* add label length, but account for the format character */
+ path_length += strlen(label) - 1;
+ }
+ va_end(list);
+
+ path = malloc(path_length + 1);
+ if (!path)
+ return -ENOMEM;
+
+ path_pos = path;
+ label_pos = labels;
+
+ for (template_pos = path_template; *template_pos; ) {
+ sep = strchrnul(template_pos, '%');
+ path_pos = mempcpy(path_pos, template_pos, sep - template_pos);
+ if (!*sep)
+ break;
+
+ path_pos = stpcpy(path_pos, *label_pos++);
+ template_pos = sep + 1;
+ }
+
+ *path_pos = 0;
+ *out = path;
+ return 0;
+}
+
+_public_ int sd_bus_path_decode_many(const char *path, const char *path_template, ...) {
+ _cleanup_strv_free_ char **labels = NULL;
+ const char *template_pos, *path_pos;
+ char **label_pos;
+ va_list list;
+ int r;
+
+ /*
+ * This decodes an object-path based on a template argument. The
+ * template consists of a verbatim path, optionally including special
+ * directives:
+ *
+ * - Each occurrence of '%' in the template matches an arbitrary
+ * substring of a label in the given path. At most one such
+ * directive is allowed per label. For each such directive, the
+ * caller must provide an output parameter (char **) via va_arg. If
+ * NULL is passed, the given label is verified, but not returned.
+ * For each matched label, the *decoded* label is stored in the
+ * passed output argument, and the caller is responsible to free
+ * it. Note that the output arguments are only modified if the
+ * actualy path matched the template. Otherwise, they're left
+ * untouched.
+ *
+ * This function returns <0 on error, 0 if the path does not match the
+ * template, 1 if it matched.
+ */
+
+ assert_return(path, -EINVAL);
+ assert_return(path_template, -EINVAL);
+
+ path_pos = path;
+
+ for (template_pos = path_template; *template_pos; ) {
+ const char *sep;
+ size_t length;
+ char *label;
+
+ /* verify everything until the next '%' matches verbatim */
+ sep = strchrnul(template_pos, '%');
+ length = sep - template_pos;
+ if (strncmp(path_pos, template_pos, length))
+ return 0;
+
+ path_pos += length;
+ template_pos += length;
+
+ if (!*template_pos)
+ break;
+
+ /* We found the next '%' character. Everything up until here
+ * matched. We now skip ahead to the end of this label and make
+ * sure it matches the tail of the label in the path. Then we
+ * decode the string in-between and save it for later use. */
+
+ ++template_pos; /* skip over '%' */
+
+ sep = strchrnul(template_pos, '/');
+ length = sep - template_pos; /* length of suffix to match verbatim */
+
+ /* verify the suffixes match */
+ sep = strchrnul(path_pos, '/');
+ if (sep - path_pos < (ssize_t)length ||
+ strncmp(sep - length, template_pos, length))
+ return 0;
+
+ template_pos += length; /* skip over matched label */
+ length = sep - path_pos - length; /* length of sub-label to decode */
+
+ /* store unescaped label for later use */
+ label = bus_label_unescape_n(path_pos, length);
+ if (!label)
+ return -ENOMEM;
+
+ r = strv_consume(&labels, label);
+ if (r < 0)
+ return r;
+
+ path_pos = sep; /* skip decoded label and suffix */
+ }
+
+ /* end of template must match end of path */
+ if (*path_pos)
+ return 0;
+
+ /* copy the labels over to the caller */
+ va_start(list, path_template);
+ for (label_pos = labels; label_pos && *label_pos; ++label_pos) {
+ char **arg;
+
+ arg = va_arg(list, char **);
+ if (arg)
+ *arg = *label_pos;
+ else
+ free(*label_pos);
+ }
+ va_end(list);
+
+ free(labels);
+ labels = NULL;
+ return 1;
+}
+
_public_ int sd_bus_try_close(sd_bus *bus) {
int r;
diff --git a/src/libsystemd/sd-bus/test-bus-benchmark.c b/src/libsystemd/sd-bus/test-bus-benchmark.c
index d14110aa04..3a88bf59c7 100644
--- a/src/libsystemd/sd-bus/test-bus-benchmark.c
+++ b/src/libsystemd/sd-bus/test-bus-benchmark.c
@@ -21,14 +21,16 @@
#include <sys/wait.h>
-#include "def.h"
-#include "util.h"
-#include "time-util.h"
-
#include "sd-bus.h"
-#include "bus-kernel.h"
+
+#include "alloc-util.h"
#include "bus-internal.h"
+#include "bus-kernel.h"
#include "bus-util.h"
+#include "def.h"
+#include "fd-util.h"
+#include "time-util.h"
+#include "util.h"
#define MAX_SIZE (2*1024*1024)
@@ -44,7 +46,7 @@ static void server(sd_bus *b, size_t *result) {
int r;
for (;;) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
r = sd_bus_process(b, &m);
assert_se(r >= 0);
@@ -78,7 +80,7 @@ static void server(sd_bus *b, size_t *result) {
}
static void transaction(sd_bus *b, size_t sz, const char *server_name) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
uint8_t *p;
assert_se(sd_bus_message_new_method_call(b, &m, server_name, "/", "benchmark.server", "Work") >= 0);
@@ -90,7 +92,7 @@ static void transaction(sd_bus *b, size_t sz, const char *server_name) {
}
static void client_bisect(const char *address, const char *server_name) {
- _cleanup_bus_message_unref_ sd_bus_message *x = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *x = NULL;
size_t lsize, rsize, csize;
sd_bus *b;
int r;
@@ -164,7 +166,7 @@ static void client_bisect(const char *address, const char *server_name) {
}
static void client_chart(Type type, const char *address, const char *server_name, int fd) {
- _cleanup_bus_message_unref_ sd_bus_message *x = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *x = NULL;
size_t csize;
sd_bus *b;
int r;
diff --git a/src/libsystemd/sd-bus/test-bus-chat.c b/src/libsystemd/sd-bus/test-bus-chat.c
index 20f1b532b7..b07e348fdb 100644
--- a/src/libsystemd/sd-bus/test-bus-chat.c
+++ b/src/libsystemd/sd-bus/test-bus-chat.c
@@ -19,21 +19,23 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
+#include <fcntl.h>
#include <pthread.h>
+#include <stdlib.h>
#include <unistd.h>
-#include <fcntl.h>
-
-#include "log.h"
-#include "util.h"
-#include "macro.h"
-#include "formats-util.h"
#include "sd-bus.h"
+
+#include "alloc-util.h"
#include "bus-error.h"
-#include "bus-match.h"
#include "bus-internal.h"
+#include "bus-match.h"
#include "bus-util.h"
+#include "fd-util.h"
+#include "formats-util.h"
+#include "log.h"
+#include "macro.h"
+#include "util.h"
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)));
@@ -128,7 +130,7 @@ static int server(sd_bus *bus) {
bool client1_gone = false, client2_gone = false;
while (!client1_gone || !client2_gone) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
pid_t pid = 0;
const char *label = NULL;
@@ -259,9 +261,9 @@ fail:
}
static void* client1(void*p) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
const char *hello;
int r;
_cleanup_close_pair_ int pp[2] = { -1, -1 };
@@ -329,7 +331,7 @@ static void* client1(void*p) {
finish:
if (bus) {
- _cleanup_bus_message_unref_ sd_bus_message *q;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *q;
r = sd_bus_message_new_method_call(
bus,
@@ -358,9 +360,9 @@ static int quit_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_er
}
static void* client2(void*p) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
bool quit = false;
const char *mid;
int r;
@@ -497,7 +499,7 @@ static void* client2(void*p) {
finish:
if (bus) {
- _cleanup_bus_message_unref_ sd_bus_message *q;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *q;
r = sd_bus_message_new_method_call(
bus,
diff --git a/src/libsystemd/sd-bus/test-bus-cleanup.c b/src/libsystemd/sd-bus/test-bus-cleanup.c
index f586880593..cbc450fdb2 100644
--- a/src/libsystemd/sd-bus/test-bus-cleanup.c
+++ b/src/libsystemd/sd-bus/test-bus-cleanup.c
@@ -22,20 +22,21 @@
#include <stdio.h>
#include "sd-bus.h"
-#include "bus-util.h"
+
#include "bus-internal.h"
#include "bus-message.h"
+#include "bus-util.h"
#include "refcnt.h"
static void test_bus_new(void) {
- _cleanup_bus_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
assert_se(sd_bus_new(&bus) == 0);
printf("after new: refcount %u\n", REFCNT_GET(bus->n_ref));
}
static int test_bus_open(void) {
- _cleanup_bus_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
r = sd_bus_open_system(&bus);
@@ -50,7 +51,7 @@ static int test_bus_open(void) {
static void test_bus_new_method_call(void) {
sd_bus *bus = NULL;
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
assert_se(sd_bus_open_system(&bus) >= 0);
@@ -58,13 +59,13 @@ static void test_bus_new_method_call(void) {
printf("after message_new_method_call: refcount %u\n", REFCNT_GET(bus->n_ref));
- sd_bus_unref(bus);
- printf("after bus_unref: refcount %u\n", m->n_ref);
+ sd_bus_flush_close_unref(bus);
+ printf("after bus_flush_close_unref: refcount %u\n", m->n_ref);
}
static void test_bus_new_signal(void) {
sd_bus *bus = NULL;
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
assert_se(sd_bus_open_system(&bus) >= 0);
@@ -72,8 +73,8 @@ static void test_bus_new_signal(void) {
printf("after message_new_signal: refcount %u\n", REFCNT_GET(bus->n_ref));
- sd_bus_unref(bus);
- printf("after bus_unref: refcount %u\n", m->n_ref);
+ sd_bus_flush_close_unref(bus);
+ printf("after bus_flush_close_unref: refcount %u\n", m->n_ref);
}
int main(int argc, char **argv) {
diff --git a/src/libsystemd/sd-bus/test-bus-creds.c b/src/libsystemd/sd-bus/test-bus-creds.c
index 580117165a..8003501059 100644
--- a/src/libsystemd/sd-bus/test-bus-creds.c
+++ b/src/libsystemd/sd-bus/test-bus-creds.c
@@ -20,15 +20,16 @@
***/
#include "sd-bus.h"
+
#include "bus-dump.h"
#include "bus-util.h"
#include "cgroup-util.h"
int main(int argc, char *argv[]) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
int r;
- if (cg_unified() == -ENOEXEC) {
+ if (cg_unified() == -ENOMEDIUM) {
puts("Skipping test: /sys/fs/cgroup/ not available");
return EXIT_TEST_SKIP;
}
diff --git a/src/libsystemd/sd-bus/test-bus-error.c b/src/libsystemd/sd-bus/test-bus-error.c
index 5753c04b0e..407fd14555 100644
--- a/src/libsystemd/sd-bus/test-bus-error.c
+++ b/src/libsystemd/sd-bus/test-bus-error.c
@@ -20,13 +20,14 @@
***/
#include "sd-bus.h"
+
+#include "bus-common-errors.h"
#include "bus-error.h"
#include "bus-util.h"
#include "errno-list.h"
-#include "bus-common-errors.h"
static void test_error(void) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL, second = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL, second = SD_BUS_ERROR_NULL;
const sd_bus_error const_error = SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_EXISTS, "const error");
const sd_bus_error temporarily_const_error = {
.name = SD_BUS_ERROR_ACCESS_DENIED,
@@ -43,7 +44,15 @@ static void test_error(void) {
assert_se(sd_bus_error_is_set(&error));
sd_bus_error_free(&error);
+ /* Check with no error */
+ assert_se(!sd_bus_error_is_set(&error));
+ assert_se(sd_bus_error_setf(&error, NULL, "yyy %i", -1) == 0);
+ assert_se(error.name == NULL);
+ assert_se(error.message == NULL);
+ assert_se(!sd_bus_error_has_name(&error, SD_BUS_ERROR_FILE_NOT_FOUND));
+ assert_se(sd_bus_error_get_errno(&error) == 0);
assert_se(!sd_bus_error_is_set(&error));
+
assert_se(sd_bus_error_setf(&error, SD_BUS_ERROR_FILE_NOT_FOUND, "yyy %i", -1) == -ENOENT);
assert_se(streq(error.name, SD_BUS_ERROR_FILE_NOT_FOUND));
assert_se(streq(error.message, "yyy -1"));
@@ -111,6 +120,16 @@ static void test_error(void) {
assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_IO_ERROR));
assert_se(sd_bus_error_get_errno(&error) == EIO);
assert_se(sd_bus_error_is_set(&error));
+ sd_bus_error_free(&error);
+
+ /* Check with no error */
+ assert_se(!sd_bus_error_is_set(&error));
+ assert_se(sd_bus_error_set_errnof(&error, 0, "Waldi %c", 'X') == 0);
+ assert_se(error.name == NULL);
+ assert_se(error.message == NULL);
+ assert_se(!sd_bus_error_has_name(&error, SD_BUS_ERROR_IO_ERROR));
+ assert_se(sd_bus_error_get_errno(&error) == 0);
+ assert_se(!sd_bus_error_is_set(&error));
}
extern const sd_bus_error_map __start_BUS_ERROR_MAP[];
@@ -166,6 +185,16 @@ static const sd_bus_error_map test_errors4[] = {
SD_BUS_ERROR_MAP_END
};
+static const sd_bus_error_map test_errors_bad1[] = {
+ SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-1", 0),
+ SD_BUS_ERROR_MAP_END
+};
+
+static const sd_bus_error_map test_errors_bad2[] = {
+ SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-1", -1),
+ SD_BUS_ERROR_MAP_END
+};
+
static void test_errno_mapping_custom(void) {
assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error", NULL) == -5);
assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-2", NULL) == -52);
@@ -189,6 +218,9 @@ static void test_errno_mapping_custom(void) {
assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-y", NULL) == -EIO);
assert_se(sd_bus_error_set(NULL, BUS_ERROR_NO_SUCH_UNIT, NULL) == -ENOENT);
+
+ assert_se(sd_bus_error_add_map(test_errors_bad1) == -EINVAL);
+ assert_se(sd_bus_error_add_map(test_errors_bad2) == -EINVAL);
}
int main(int argc, char *argv[]) {
diff --git a/src/libsystemd/sd-bus/test-bus-gvariant.c b/src/libsystemd/sd-bus/test-bus-gvariant.c
index b078bdc5f6..2d8e9d2f28 100644
--- a/src/libsystemd/sd-bus/test-bus-gvariant.c
+++ b/src/libsystemd/sd-bus/test-bus-gvariant.c
@@ -23,14 +23,16 @@
#include <glib.h>
#endif
-#include "util.h"
-#include "macro.h"
#include "sd-bus.h"
+
+#include "alloc-util.h"
+#include "bus-dump.h"
#include "bus-gvariant.h"
-#include "bus-util.h"
#include "bus-internal.h"
#include "bus-message.h"
-#include "bus-dump.h"
+#include "bus-util.h"
+#include "macro.h"
+#include "util.h"
static void test_bus_gvariant_is_fixed_size(void) {
assert_se(bus_gvariant_is_fixed_size("") > 0);
@@ -131,8 +133,8 @@ static void test_bus_gvariant_get_alignment(void) {
}
static void test_marshal(void) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *n = NULL;
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *n = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_free_ void *blob;
size_t sz;
int r;
diff --git a/src/libsystemd/sd-bus/test-bus-introspect.c b/src/libsystemd/sd-bus/test-bus-introspect.c
index f39dedeb24..26ba16d119 100644
--- a/src/libsystemd/sd-bus/test-bus-introspect.c
+++ b/src/libsystemd/sd-bus/test-bus-introspect.c
@@ -19,8 +19,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "log.h"
#include "bus-introspect.h"
+#include "log.h"
static int prop_get(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error) {
return -EINVAL;
diff --git a/src/libsystemd/sd-bus/test-bus-kernel-bloom.c b/src/libsystemd/sd-bus/test-bus-kernel-bloom.c
index f3d1099dd2..0e957253c0 100644
--- a/src/libsystemd/sd-bus/test-bus-kernel-bloom.c
+++ b/src/libsystemd/sd-bus/test-bus-kernel-bloom.c
@@ -19,12 +19,14 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
-#include "log.h"
-
#include "sd-bus.h"
+
+#include "alloc-util.h"
#include "bus-kernel.h"
#include "bus-util.h"
+#include "fd-util.h"
+#include "log.h"
+#include "util.h"
static int test_match(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
int *found = userdata;
@@ -45,7 +47,7 @@ static void test_one(
_cleanup_close_ int bus_ref = -1;
_cleanup_free_ char *name = NULL, *bus_name = NULL, *address = NULL;
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
sd_bus *a, *b;
int r, found = 0;
diff --git a/src/libsystemd/sd-bus/test-bus-kernel.c b/src/libsystemd/sd-bus/test-bus-kernel.c
index 6506eaab2e..6163c7784e 100644
--- a/src/libsystemd/sd-bus/test-bus-kernel.c
+++ b/src/libsystemd/sd-bus/test-bus-kernel.c
@@ -21,19 +21,21 @@
#include <fcntl.h>
-#include "util.h"
-#include "log.h"
-
#include "sd-bus.h"
+
+#include "alloc-util.h"
+#include "bus-dump.h"
#include "bus-kernel.h"
#include "bus-util.h"
-#include "bus-dump.h"
+#include "fd-util.h"
+#include "log.h"
+#include "util.h"
int main(int argc, char *argv[]) {
_cleanup_close_ int bus_ref = -1;
_cleanup_free_ char *name = NULL, *bus_name = NULL, *address = NULL, *bname = NULL;
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
const char *ua = NULL, *ub = NULL, *the_string = NULL;
sd_bus *a, *b;
int r, pipe_fds[2];
diff --git a/src/libsystemd/sd-bus/test-bus-marshal.c b/src/libsystemd/sd-bus/test-bus-marshal.c
index b203707f27..b9d1ea5217 100644
--- a/src/libsystemd/sd-bus/test-bus-marshal.c
+++ b/src/libsystemd/sd-bus/test-bus-marshal.c
@@ -19,8 +19,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
#include <math.h>
+#include <stdlib.h>
#ifdef HAVE_GLIB
#include <gio/gio.h>
@@ -30,14 +30,17 @@
#include <dbus/dbus.h>
#endif
-#include "log.h"
-#include "util.h"
-
#include "sd-bus.h"
-#include "bus-message.h"
-#include "bus-util.h"
+
+#include "alloc-util.h"
#include "bus-dump.h"
#include "bus-label.h"
+#include "bus-message.h"
+#include "bus-util.h"
+#include "fd-util.h"
+#include "hexdecoct.h"
+#include "log.h"
+#include "util.h"
static void test_bus_path_encode_unique(void) {
_cleanup_free_ char *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL;
@@ -66,6 +69,36 @@ static void test_bus_path_encode(void) {
assert_se(sd_bus_path_decode(e, "/foo/bar", &f) > 0 && streq(f, "foo.bar"));
}
+static void test_bus_path_encode_many(void) {
+ _cleanup_free_ char *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL, *f = NULL;
+
+ assert_se(sd_bus_path_decode_many("/foo/bar", "/prefix/%", NULL) == 0);
+ assert_se(sd_bus_path_decode_many("/prefix/bar", "/prefix/%bar", NULL) == 1);
+ assert_se(sd_bus_path_decode_many("/foo/bar", "/prefix/%/suffix", NULL) == 0);
+ assert_se(sd_bus_path_decode_many("/prefix/foobar/suffix", "/prefix/%/suffix", &a) == 1 && streq_ptr(a, "foobar"));
+ assert_se(sd_bus_path_decode_many("/prefix/one_foo_two/mid/three_bar_four/suffix", "/prefix/one_%_two/mid/three_%_four/suffix", &b, &c) == 1 && streq_ptr(b, "foo") && streq_ptr(c, "bar"));
+ assert_se(sd_bus_path_decode_many("/prefix/one_foo_two/mid/three_bar_four/suffix", "/prefix/one_%_two/mid/three_%_four/suffix", NULL, &d) == 1 && streq_ptr(d, "bar"));
+
+ assert_se(sd_bus_path_decode_many("/foo/bar", "/foo/bar/%", NULL) == 0);
+ assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/bar%", NULL) == 0);
+ assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/%/bar", NULL) == 0);
+ assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/%bar", NULL) == 0);
+ assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/bar/suffix") == 1);
+ assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/%%/suffix", NULL, NULL) == 0); /* multiple '%' are treated verbatim */
+ assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/%/suffi", NULL) == 0);
+ assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/%/suffix", &e) == 1 && streq_ptr(e, "bar"));
+ assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/%/%", NULL, NULL) == 1);
+ assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/%/%/%", NULL, NULL, NULL) == 1);
+ assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "%/%/%", NULL, NULL, NULL) == 0);
+ assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/%/%", NULL, NULL) == 0);
+ assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/%/%/", NULL, NULL) == 0);
+ assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/%/", NULL) == 0);
+ assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/%", NULL) == 0);
+ assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "%", NULL) == 0);
+
+ assert_se(sd_bus_path_encode_many(&f, "/prefix/one_%_two/mid/three_%_four/suffix", "foo", "bar") >= 0 && streq_ptr(f, "/prefix/one_foo_two/mid/three_bar_four/suffix"));
+}
+
static void test_bus_label_escape_one(const char *a, const char *b) {
_cleanup_free_ char *t = NULL, *x = NULL, *y = NULL;
@@ -90,7 +123,7 @@ static void test_bus_label_escape(void) {
}
int main(int argc, char *argv[]) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *copy = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *copy = NULL;
int r, boolean;
const char *x, *x2, *y, *z, *a, *b, *c, *d, *a_signature;
uint8_t u, v;
@@ -102,7 +135,7 @@ int main(int argc, char *argv[]) {
_cleanup_free_ char *first = NULL, *second = NULL, *third = NULL;
_cleanup_fclose_ FILE *ms = NULL;
size_t first_size = 0, second_size = 0, third_size = 0;
- _cleanup_bus_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
double dbl;
uint64_t u64;
@@ -213,6 +246,8 @@ int main(int argc, char *argv[]) {
log_error("%s", error.message);
else
dbus_message_unref(w);
+
+ dbus_error_free(&error);
}
#endif
@@ -393,6 +428,7 @@ int main(int argc, char *argv[]) {
test_bus_label_escape();
test_bus_path_encode();
test_bus_path_encode_unique();
+ test_bus_path_encode_many();
return 0;
}
diff --git a/src/libsystemd/sd-bus/test-bus-match.c b/src/libsystemd/sd-bus/test-bus-match.c
index 75ea28371b..97ef396c6b 100644
--- a/src/libsystemd/sd-bus/test-bus-match.c
+++ b/src/libsystemd/sd-bus/test-bus-match.c
@@ -19,13 +19,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "log.h"
-#include "macro.h"
-
#include "bus-match.h"
#include "bus-message.h"
-#include "bus-util.h"
#include "bus-slot.h"
+#include "bus-util.h"
+#include "log.h"
+#include "macro.h"
static bool mask[32];
@@ -91,8 +90,8 @@ int main(int argc, char *argv[]) {
.type = BUS_MATCH_ROOT,
};
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
enum bus_match_node_type i;
sd_bus_slot slots[19];
int r;
diff --git a/src/libsystemd/sd-bus/test-bus-objects.c b/src/libsystemd/sd-bus/test-bus-objects.c
index 0a35b750b3..0dd248552c 100644
--- a/src/libsystemd/sd-bus/test-bus-objects.c
+++ b/src/libsystemd/sd-bus/test-bus-objects.c
@@ -19,19 +19,20 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
#include <pthread.h>
-
-#include "log.h"
-#include "util.h"
-#include "macro.h"
-#include "strv.h"
+#include <stdlib.h>
#include "sd-bus.h"
+
+#include "alloc-util.h"
+#include "bus-dump.h"
#include "bus-internal.h"
#include "bus-message.h"
#include "bus-util.h"
-#include "bus-dump.h"
+#include "log.h"
+#include "macro.h"
+#include "strv.h"
+#include "util.h"
struct context {
int fds[2];
@@ -296,9 +297,9 @@ fail:
}
static int client(struct context *c) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_bus_unref_ sd_bus *bus = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
const char *s;
int r;
diff --git a/src/libsystemd/sd-bus/test-bus-proxy.c b/src/libsystemd/sd-bus/test-bus-proxy.c
index aef768dc18..42fa3e6293 100644
--- a/src/libsystemd/sd-bus/test-bus-proxy.c
+++ b/src/libsystemd/sd-bus/test-bus-proxy.c
@@ -23,13 +23,14 @@
#include <fcntl.h>
#include <stdlib.h>
-#include "util.h"
-#include "log.h"
-
#include "sd-bus.h"
+
+#include "alloc-util.h"
+#include "bus-dump.h"
#include "bus-kernel.h"
#include "bus-util.h"
-#include "bus-dump.h"
+#include "log.h"
+#include "util.h"
typedef struct {
const char *sender;
@@ -52,7 +53,7 @@ static int test_proxy_acquired(sd_bus_message *m, void *userdata, sd_bus_error *
}
static void test_proxy_matched(void) {
- _cleanup_bus_flush_close_unref_ sd_bus *a = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *a = NULL;
_cleanup_free_ char *matchstr = NULL;
TestProxyMatch match = {};
const char *me;
diff --git a/src/libsystemd/sd-bus/test-bus-server.c b/src/libsystemd/sd-bus/test-bus-server.c
index 080d8eddb7..bd89780346 100644
--- a/src/libsystemd/sd-bus/test-bus-server.c
+++ b/src/libsystemd/sd-bus/test-bus-server.c
@@ -19,16 +19,16 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
#include <pthread.h>
-
-#include "log.h"
-#include "util.h"
-#include "macro.h"
+#include <stdlib.h>
#include "sd-bus.h"
+
#include "bus-internal.h"
#include "bus-util.h"
+#include "log.h"
+#include "macro.h"
+#include "util.h"
struct context {
int fds[2];
@@ -57,7 +57,7 @@ static void *server(void *p) {
assert_se(sd_bus_start(bus) >= 0);
while (!quit) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
r = sd_bus_process(bus, &m);
if (r < 0) {
@@ -124,8 +124,8 @@ fail:
}
static int client(struct context *c) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
- _cleanup_bus_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
+ _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
diff --git a/src/libsystemd/sd-bus/test-bus-signature.c b/src/libsystemd/sd-bus/test-bus-signature.c
index 17c6188ca0..949d16e6e9 100644
--- a/src/libsystemd/sd-bus/test-bus-signature.c
+++ b/src/libsystemd/sd-bus/test-bus-signature.c
@@ -19,10 +19,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-
-#include "log.h"
-#include "bus-signature.h"
#include "bus-internal.h"
+#include "bus-signature.h"
+#include "log.h"
+#include "string-util.h"
int main(int argc, char *argv[]) {
char prefix[256];
diff --git a/src/libsystemd/sd-bus/test-bus-zero-copy.c b/src/libsystemd/sd-bus/test-bus-zero-copy.c
index 2d062fc9b5..1cf8416fa4 100644
--- a/src/libsystemd/sd-bus/test-bus-zero-copy.c
+++ b/src/libsystemd/sd-bus/test-bus-zero-copy.c
@@ -21,14 +21,17 @@
#include <sys/mman.h>
-#include "util.h"
-#include "log.h"
-#include "memfd-util.h"
-
#include "sd-bus.h"
-#include "bus-message.h"
-#include "bus-kernel.h"
+
+#include "alloc-util.h"
#include "bus-dump.h"
+#include "bus-kernel.h"
+#include "bus-message.h"
+#include "fd-util.h"
+#include "log.h"
+#include "memfd-util.h"
+#include "string-util.h"
+#include "util.h"
#define FIRST_ARRAY 17
#define SECOND_ARRAY 33
diff --git a/src/libsystemd/sd-daemon/sd-daemon.c b/src/libsystemd/sd-daemon/sd-daemon.c
index 9ec73406c6..f1e9b7ed1b 100644
--- a/src/libsystemd/sd-daemon/sd-daemon.c
+++ b/src/libsystemd/sd-daemon/sd-daemon.c
@@ -19,29 +19,46 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <netinet/in.h>
-#include <stdlib.h>
#include <errno.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stddef.h>
#include <limits.h>
#include <mqueue.h>
+#include <netinet/in.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <unistd.h>
-#include "util.h"
+#include "sd-daemon.h"
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "parse-util.h"
#include "path-util.h"
#include "socket-util.h"
-#include "sd-daemon.h"
+#include "strv.h"
+#include "util.h"
+
+#define SNDBUF_SIZE (8*1024*1024)
+
+static void unsetenv_all(bool unset_environment) {
+
+ if (!unset_environment)
+ return;
+
+ unsetenv("LISTEN_PID");
+ unsetenv("LISTEN_FDS");
+ unsetenv("LISTEN_FDNAMES");
+}
_public_ int sd_listen_fds(int unset_environment) {
const char *e;
- unsigned n;
- int r, fd;
+ int n, r, fd;
pid_t pid;
e = getenv("LISTEN_PID");
@@ -66,25 +83,68 @@ _public_ int sd_listen_fds(int unset_environment) {
goto finish;
}
- r = safe_atou(e, &n);
+ r = safe_atoi(e, &n);
if (r < 0)
goto finish;
- for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int) n; fd ++) {
+ assert_cc(SD_LISTEN_FDS_START < INT_MAX);
+ if (n <= 0 || n > INT_MAX - SD_LISTEN_FDS_START) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++) {
r = fd_cloexec(fd, true);
if (r < 0)
goto finish;
}
- r = (int) n;
+ r = n;
finish:
- if (unset_environment) {
- unsetenv("LISTEN_PID");
- unsetenv("LISTEN_FDS");
+ unsetenv_all(unset_environment);
+ return r;
+}
+
+_public_ int sd_listen_fds_with_names(int unset_environment, char ***names) {
+ _cleanup_strv_free_ char **l = NULL;
+ bool have_names;
+ int n_names = 0, n_fds;
+ const char *e;
+ int r;
+
+ if (!names)
+ return sd_listen_fds(unset_environment);
+
+ e = getenv("LISTEN_FDNAMES");
+ if (e) {
+ n_names = strv_split_extract(&l, e, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
+ if (n_names < 0) {
+ unsetenv_all(unset_environment);
+ return n_names;
+ }
+
+ have_names = true;
+ } else
+ have_names = false;
+
+ n_fds = sd_listen_fds(unset_environment);
+ if (n_fds <= 0)
+ return n_fds;
+
+ if (have_names) {
+ if (n_names != n_fds)
+ return -EINVAL;
+ } else {
+ r = strv_extend_n(&l, "unknown", n_fds);
+ if (r < 0)
+ return r;
}
- return r;
+ *names = l;
+ l = NULL;
+
+ return n_fds;
}
_public_ int sd_is_fifo(int fd, const char *path) {
@@ -310,10 +370,15 @@ _public_ int sd_is_socket_unix(int fd, int type, int listening, const char *path
_public_ int sd_is_mq(int fd, const char *path) {
struct mq_attr attr;
- assert_return(fd >= 0, -EBADF);
+ /* Check that the fd is valid */
+ assert_return(fcntl(fd, F_GETFD) >= 0, -errno);
- if (mq_getattr(fd, &attr) < 0)
+ if (mq_getattr(fd, &attr) < 0) {
+ if (errno == EBADF)
+ /* A non-mq fd (or an invalid one, but we ruled that out above) */
+ return 0;
return -errno;
+ }
if (path) {
char fpath[PATH_MAX];
@@ -376,12 +441,19 @@ _public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char
goto finish;
}
+ if (strlen(e) > sizeof(sockaddr.un.sun_path)) {
+ r = -EINVAL;
+ goto finish;
+ }
+
fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
if (fd < 0) {
r = -errno;
goto finish;
}
+ fd_inc_sndbuf(fd, SNDBUF_SIZE);
+
iovec.iov_len = strlen(state);
strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
@@ -396,9 +468,11 @@ _public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char
if (n_fds > 0 || have_pid) {
/* CMSG_SPACE(0) may return value different then zero, which results in miscalculated controllen. */
- msghdr.msg_controllen = (n_fds ? CMSG_SPACE(sizeof(int) * n_fds) : 0) +
- CMSG_SPACE(sizeof(struct ucred)) * have_pid;
- msghdr.msg_control = alloca(msghdr.msg_controllen);
+ msghdr.msg_controllen =
+ (n_fds > 0 ? CMSG_SPACE(sizeof(int) * n_fds) : 0) +
+ (have_pid ? CMSG_SPACE(sizeof(struct ucred)) : 0);
+
+ msghdr.msg_control = alloca0(msghdr.msg_controllen);
cmsg = CMSG_FIRSTHDR(&msghdr);
if (n_fds > 0) {
@@ -498,16 +572,11 @@ _public_ int sd_notifyf(int unset_environment, const char *format, ...) {
}
_public_ int sd_booted(void) {
- struct stat st;
-
/* We test whether the runtime unit file directory has been
* created. This takes place in mount-setup.c, so is
* guaranteed to happen very early during boot. */
- if (lstat("/run/systemd/system/", &st) < 0)
- return 0;
-
- return !!S_ISDIR(st.st_mode);
+ return laccess("/run/systemd/system/", F_OK) >= 0;
}
_public_ int sd_watchdog_enabled(int unset_environment, uint64_t *usec) {
@@ -522,7 +591,7 @@ _public_ int sd_watchdog_enabled(int unset_environment, uint64_t *usec) {
r = safe_atou64(s, &u);
if (r < 0)
goto finish;
- if (u <= 0) {
+ if (u <= 0 || u >= USEC_INFINITY) {
r = -EINVAL;
goto finish;
}
diff --git a/src/machine-id-commit/Makefile b/src/libsystemd/sd-device/Makefile
index d0b0e8e008..d0b0e8e008 120000
--- a/src/machine-id-commit/Makefile
+++ b/src/libsystemd/sd-device/Makefile
diff --git a/src/libsystemd/sd-device/device-enumerator.c b/src/libsystemd/sd-device/device-enumerator.c
index 5eb37e16cb..4a7a8b1f9e 100644
--- a/src/libsystemd/sd-device/device-enumerator.c
+++ b/src/libsystemd/sd-device/device-enumerator.c
@@ -18,15 +18,18 @@
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 "alloc-util.h"
#include "device-enumerator-private.h"
+#include "device-util.h"
+#include "dirent-util.h"
+#include "fd-util.h"
+#include "prioq.h"
+#include "set.h"
+#include "string-util.h"
+#include "strv.h"
+#include "util.h"
#define DEVICE_ENUMERATE_MAX_DEPTH 256
@@ -56,7 +59,7 @@ struct sd_device_enumerator {
};
_public_ int sd_device_enumerator_new(sd_device_enumerator **ret) {
- _cleanup_device_enumerator_unref_ sd_device_enumerator *enumerator = NULL;
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *enumerator = NULL;
assert(ret);
@@ -484,7 +487,7 @@ static int enumerator_scan_dir_and_add_devices(sd_device_enumerator *enumerator,
return -errno;
FOREACH_DIRENT_ALL(dent, dir, return -errno) {
- _cleanup_device_unref_ sd_device *device = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
char syspath[strlen(path) + 1 + strlen(dent->d_name) + 1];
dev_t devnum;
int ifindex, initialized, k;
@@ -637,7 +640,7 @@ static int enumerator_scan_devices_tag(sd_device_enumerator *enumerator, const c
/* TODO: filter away subsystems? */
FOREACH_DIRENT_ALL(dent, dir, return -errno) {
- _cleanup_device_unref_ sd_device *device = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
const char *subsystem, *sysname;
int k;
@@ -707,7 +710,7 @@ static int enumerator_scan_devices_tags(sd_device_enumerator *enumerator) {
}
static int parent_add_child(sd_device_enumerator *enumerator, const char *path) {
- _cleanup_device_unref_ sd_device *device = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
const char *subsystem, *sysname;
int r;
@@ -812,10 +815,8 @@ static int enumerator_scan_devices_all(sd_device_enumerator *enumerator) {
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;
- }
+ if (r < 0)
+ return log_debug_errno(r, "device-enumerator: failed to scan /sys/subsystem: %m");
} else {
int k;
diff --git a/src/libsystemd/sd-device/device-private.c b/src/libsystemd/sd-device/device-private.c
index 0ec9667744..f2af3ab3ae 100644
--- a/src/libsystemd/sd-device/device-private.c
+++ b/src/libsystemd/sd-device/device-private.c
@@ -19,25 +19,31 @@
***/
#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 <sys/types.h>
#include "sd-device.h"
-#include "device-util.h"
+#include "alloc-util.h"
#include "device-internal.h"
#include "device-private.h"
+#include "device-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "hashmap.h"
+#include "macro.h"
+#include "mkdir.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "refcnt.h"
+#include "set.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "strv.h"
+#include "strxcpyx.h"
+#include "user-util.h"
+#include "util.h"
int device_add_property(sd_device *device, const char *key, const char *value) {
int r;
@@ -200,10 +206,8 @@ static int device_read_db(sd_device *device) {
if (r < 0) {
if (r == -ENOENT)
return 0;
- else {
- log_debug("sd-device: failed to read db '%s': %s", path, strerror(-r));
- return r;
- }
+ else
+ return log_debug_errno(r, "sd-device: failed to read db '%s': %m", path);
}
/* devices with a database entry are initialized */
@@ -247,7 +251,7 @@ static int device_read_db(sd_device *device) {
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));
+ log_debug_errno(r, "sd-device: failed to handle db entry '%c:%s': %m", key, value);
state = PRE_KEY;
}
@@ -549,7 +553,7 @@ static int device_verify(sd_device *device, DeviceAction action, uint64_t seqnum
}
int device_new_from_strv(sd_device **ret, char **strv) {
- _cleanup_device_unref_ sd_device *device = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
char **key;
const char *major = NULL, *minor = NULL;
DeviceAction action = _DEVICE_ACTION_INVALID;
@@ -586,7 +590,7 @@ int device_new_from_strv(sd_device **ret, char **strv) {
}
int device_new_from_nulstr(sd_device **ret, uint8_t *nulstr, size_t len) {
- _cleanup_device_unref_ sd_device *device = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
const char *major = NULL, *minor = NULL;
DeviceAction action = _DEVICE_ACTION_INVALID;
uint64_t seqnum;
@@ -789,7 +793,7 @@ int device_rename(sd_device *device, const char *name) {
}
int device_shallow_clone(sd_device *old_device, sd_device **new_device) {
- _cleanup_device_unref_ sd_device *ret = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *ret = NULL;
int r;
assert(old_device);
@@ -816,7 +820,7 @@ 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) {
- _cleanup_device_unref_ sd_device *ret = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *ret = NULL;
int r;
assert(old_device);
@@ -839,7 +843,7 @@ int device_clone_with_db(sd_device *old_device, sd_device **new_device) {
}
int device_new_from_synthetic_event(sd_device **new_device, const char *syspath, const char *action) {
- _cleanup_device_unref_ sd_device *ret = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *ret = NULL;
int r;
assert(new_device);
diff --git a/src/libsystemd/sd-device/device-private.h b/src/libsystemd/sd-device/device-private.h
index 49a7b66a2b..d1f34efc2d 100644
--- a/src/libsystemd/sd-device/device-private.h
+++ b/src/libsystemd/sd-device/device-private.h
@@ -21,6 +21,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <inttypes.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+#include "sd-device.h"
+
int device_new_from_nulstr(sd_device **ret, uint8_t *nulstr, size_t len);
int device_new_from_strv(sd_device **ret, char **strv);
diff --git a/src/libsystemd/sd-device/device-util.h b/src/libsystemd/sd-device/device-util.h
index 9b05a2498d..ba9edc61ce 100644
--- a/src/libsystemd/sd-device/device-util.h
+++ b/src/libsystemd/sd-device/device-util.h
@@ -23,12 +23,6 @@
#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; \
diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c
index 7cea5a0746..9633e46ce0 100644
--- a/src/libsystemd/sd-device/sd-device.c
+++ b/src/libsystemd/sd-device/sd-device.c
@@ -19,26 +19,31 @@
***/
#include <ctype.h>
-#include <sys/types.h>
#include <net/if.h>
+#include <sys/types.h>
-#include "util.h"
-#include "macro.h"
-#include "path-util.h"
-#include "strxcpyx.h"
+#include "sd-device.h"
+
+#include "alloc-util.h"
+#include "device-internal.h"
+#include "device-private.h"
+#include "device-util.h"
+#include "fd-util.h"
#include "fileio.h"
+#include "fs-util.h"
#include "hashmap.h"
+#include "macro.h"
+#include "parse-util.h"
+#include "path-util.h"
#include "set.h"
+#include "stat-util.h"
+#include "string-util.h"
#include "strv.h"
-
-#include "sd-device.h"
-
-#include "device-util.h"
-#include "device-private.h"
-#include "device-internal.h"
+#include "strxcpyx.h"
+#include "util.h"
int device_new_aux(sd_device **ret) {
- _cleanup_device_unref_ sd_device *device = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
assert(ret);
@@ -169,11 +174,10 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) {
/* the device does not exist (any more?) */
return -ENODEV;
- log_debug("sd-device: could not canonicalize '%s': %m", _syspath);
- return -errno;
+ return log_debug_errno(errno, "sd-device: could not canonicalize '%s': %m", _syspath);
}
} else if (r < 0) {
- log_debug("sd-device: could not get target of '%s': %s", _syspath, strerror(-r));
+ log_debug_errno(r, "sd-device: could not get target of '%s': %m", _syspath);
return r;
}
@@ -218,7 +222,7 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) {
}
_public_ int sd_device_new_from_syspath(sd_device **ret, const char *syspath) {
- _cleanup_device_unref_ sd_device *device = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
int r;
assert_return(ret, -EINVAL);
@@ -296,15 +300,27 @@ _public_ int sd_device_new_from_subsystem_sysname(sd_device **ret, const char *s
} else
return -EINVAL;
} else {
- syspath = strjoina("/sys/subsystem/", subsystem, "/devices/", sysname);
+ char *name;
+ size_t len = 0;
+
+ /* translate sysname back to sysfs filename */
+ name = strdupa(sysname);
+ while (name[len] != '\0') {
+ if (name[len] == '/')
+ name[len] = '!';
+
+ len ++;
+ }
+
+ syspath = strjoina("/sys/subsystem/", subsystem, "/devices/", name);
if (access(syspath, F_OK) >= 0)
return sd_device_new_from_syspath(ret, syspath);
- syspath = strjoina("/sys/bus/", subsystem, "/devices/", sysname);
+ syspath = strjoina("/sys/bus/", subsystem, "/devices/", name);
if (access(syspath, F_OK) >= 0)
return sd_device_new_from_syspath(ret, syspath);
- syspath = strjoina("/sys/class/", subsystem, "/", sysname);
+ syspath = strjoina("/sys/class/", subsystem, "/", name);
if (access(syspath, F_OK) >= 0)
return sd_device_new_from_syspath(ret, syspath);
}
@@ -340,13 +356,10 @@ int device_set_ifindex(sd_device *device, const char *_ifindex) {
assert(device);
assert(_ifindex);
- r = safe_atoi(_ifindex, &ifindex);
+ r = parse_ifindex(_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;
@@ -481,7 +494,7 @@ static int handle_uevent_line(sd_device *device, const char *key, const char *va
int device_read_uevent_file(sd_device *device) {
_cleanup_free_ char *uevent = NULL;
- const char *syspath, *key, *value, *major = NULL, *minor = NULL;
+ const char *syspath, *key = NULL, *value = NULL, *major = NULL, *minor = NULL;
char *path;
size_t uevent_len;
unsigned i;
@@ -516,7 +529,7 @@ int device_read_uevent_file(sd_device *device) {
/* 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));
+ log_debug_errno(r, "sd-device: failed to read uevent file '%s': %m", path);
return r;
}
@@ -555,7 +568,7 @@ int device_read_uevent_file(sd_device *device) {
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));
+ log_debug_errno(r, "sd-device: failed to handle uevent entry '%s=%s': %m", key, value);
state = PRE_KEY;
}
@@ -569,7 +582,7 @@ int device_read_uevent_file(sd_device *device) {
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));
+ log_debug_errno(r, "sd-device: could not set 'MAJOR=%s' or 'MINOR=%s' from '%s': %m", major, minor, path);
}
return 0;
@@ -611,16 +624,14 @@ _public_ int sd_device_new_from_device_id(sd_device **ret, const char *id) {
}
case 'n':
{
- _cleanup_device_unref_ sd_device *device = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
_cleanup_close_ int sk = -1;
struct ifreq ifr = {};
int ifindex;
- r = safe_atoi(&id[1], &ifr.ifr_ifindex);
+ r = parse_ifindex(&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)
@@ -1271,10 +1282,8 @@ int device_read_db_aux(sd_device *device, bool force) {
if (r < 0) {
if (r == -ENOENT)
return 0;
- else {
- log_debug("sd-device: failed to read db '%s': %s", path, strerror(-r));
- return r;
- }
+ else
+ return log_debug_errno(r, "sd-device: failed to read db '%s': %m", path);
}
/* devices with a database entry are initialized */
@@ -1318,7 +1327,7 @@ int device_read_db_aux(sd_device *device, bool force) {
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));
+ log_debug_errno(r, "sd-device: failed to handle db entry '%c:%s': %m", key, value);
state = PRE_KEY;
}
diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c
index 48a5219275..11c7330b9b 100644
--- a/src/libsystemd/sd-event/sd-event.c
+++ b/src/libsystemd/sd-event/sd-event.c
@@ -23,19 +23,24 @@
#include <sys/timerfd.h>
#include <sys/wait.h>
-#include "sd-id128.h"
#include "sd-daemon.h"
-#include "macro.h"
-#include "prioq.h"
+#include "sd-event.h"
+#include "sd-id128.h"
+
+#include "alloc-util.h"
+#include "fd-util.h"
#include "hashmap.h"
-#include "util.h"
-#include "time-util.h"
+#include "list.h"
+#include "macro.h"
#include "missing.h"
+#include "prioq.h"
+#include "process-util.h"
#include "set.h"
-#include "list.h"
#include "signal-util.h"
-
-#include "sd-event.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "time-util.h"
+#include "util.h"
#define DEFAULT_ACCURACY_USEC (250 * USEC_PER_MSEC)
@@ -56,6 +61,23 @@ typedef enum EventSourceType {
_SOURCE_EVENT_SOURCE_TYPE_INVALID = -1
} EventSourceType;
+static const char* const event_source_type_table[_SOURCE_EVENT_SOURCE_TYPE_MAX] = {
+ [SOURCE_IO] = "io",
+ [SOURCE_TIME_REALTIME] = "realtime",
+ [SOURCE_TIME_BOOTTIME] = "bootime",
+ [SOURCE_TIME_MONOTONIC] = "monotonic",
+ [SOURCE_TIME_REALTIME_ALARM] = "realtime-alarm",
+ [SOURCE_TIME_BOOTTIME_ALARM] = "boottime-alarm",
+ [SOURCE_SIGNAL] = "signal",
+ [SOURCE_CHILD] = "child",
+ [SOURCE_DEFER] = "defer",
+ [SOURCE_POST] = "post",
+ [SOURCE_EXIT] = "exit",
+ [SOURCE_WATCHDOG] = "watchdog",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(event_source_type, int);
+
/* All objects we use in epoll events start with this value, so that
* we know how to dispatch it */
typedef enum WakeupType {
@@ -203,6 +225,7 @@ struct sd_event {
bool exit_requested:1;
bool need_process_child:1;
bool watchdog:1;
+ bool profile_delays:1;
int exit_code;
@@ -214,6 +237,9 @@ struct sd_event {
unsigned n_sources;
LIST_HEAD(sd_event_source, sources);
+
+ usec_t last_run, last_log;
+ unsigned delays[sizeof(usec_t) * 8];
};
static void source_disconnect(sd_event_source *s);
@@ -242,12 +268,6 @@ static int pending_prioq_compare(const void *a, const void *b) {
if (x->pending_iteration > y->pending_iteration)
return 1;
- /* Stability for the rest */
- if (x < y)
- return -1;
- if (x > y)
- return 1;
-
return 0;
}
@@ -277,12 +297,6 @@ static int prepare_prioq_compare(const void *a, const void *b) {
if (x->priority > y->priority)
return 1;
- /* Stability for the rest */
- if (x < y)
- return -1;
- if (x > y)
- return 1;
-
return 0;
}
@@ -310,12 +324,6 @@ static int earliest_time_prioq_compare(const void *a, const void *b) {
if (x->time.next > y->time.next)
return 1;
- /* Stability for the rest */
- if (x < y)
- return -1;
- if (x > y)
- return 1;
-
return 0;
}
@@ -343,12 +351,6 @@ static int latest_time_prioq_compare(const void *a, const void *b) {
if (x->time.next + x->time.accuracy > y->time.next + y->time.accuracy)
return 1;
- /* Stability for the rest */
- if (x < y)
- return -1;
- if (x > y)
- return 1;
-
return 0;
}
@@ -370,12 +372,6 @@ static int exit_prioq_compare(const void *a, const void *b) {
if (x->priority > y->priority)
return 1;
- /* Stability for the rest */
- if (x < y)
- return -1;
- if (x > y)
- return 1;
-
return 0;
}
@@ -442,11 +438,9 @@ _public_ int sd_event_new(sd_event** ret) {
e->original_pid = getpid();
e->perturb = USEC_INFINITY;
- e->pending = prioq_new(pending_prioq_compare);
- if (!e->pending) {
- r = -ENOMEM;
+ r = prioq_ensure_allocated(&e->pending, pending_prioq_compare);
+ if (r < 0)
goto fail;
- }
e->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (e->epoll_fd < 0) {
@@ -454,6 +448,11 @@ _public_ int sd_event_new(sd_event** ret) {
goto fail;
}
+ if (secure_getenv("SD_EVENT_PROFILE_DELAYS")) {
+ log_debug("Event loop profiling enabled. Logarithmic histogram of event loop iterations in the range 2^0 ... 2^63 us will be logged every 5s.");
+ e->profile_delays = true;
+ }
+
*ret = e;
return 0;
@@ -463,7 +462,9 @@ fail:
}
_public_ sd_event* sd_event_ref(sd_event *e) {
- assert_return(e, NULL);
+
+ if (!e)
+ return NULL;
assert(e->n_ref >= 1);
e->n_ref++;
@@ -508,7 +509,8 @@ static void source_io_unregister(sd_event_source *s) {
r = epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, s->io.fd, NULL);
if (r < 0)
- log_debug_errno(errno, "Failed to remove source %s from epoll: %m", strna(s->description));
+ log_debug_errno(errno, "Failed to remove source %s (type %s) from epoll: %m",
+ strna(s->description), event_source_type_to_string(s->type));
s->io.registered = false;
}
@@ -659,8 +661,10 @@ static int event_make_signal_data(
d->priority = priority;
r = hashmap_put(e->signal_data, &d->priority, d);
- if (r < 0)
+ if (r < 0) {
+ free(d);
return r;
+ }
added = true;
}
@@ -835,7 +839,7 @@ static void source_disconnect(sd_event_source *s) {
s->event->n_enabled_child_sources--;
}
- (void) hashmap_remove(s->event->child_sources, INT_TO_PTR(s->child.pid));
+ (void) hashmap_remove(s->event->child_sources, PID_TO_PTR(s->child.pid));
event_gc_signal_data(s->event, &s->priority, SIGCHLD);
}
@@ -1076,17 +1080,13 @@ _public_ int sd_event_add_time(
d = event_get_clock_data(e, type);
assert(d);
- if (!d->earliest) {
- d->earliest = prioq_new(earliest_time_prioq_compare);
- if (!d->earliest)
- return -ENOMEM;
- }
+ r = prioq_ensure_allocated(&d->earliest, earliest_time_prioq_compare);
+ if (r < 0)
+ return r;
- if (!d->latest) {
- d->latest = prioq_new(latest_time_prioq_compare);
- if (!d->latest)
- return -ENOMEM;
- }
+ r = prioq_ensure_allocated(&d->latest, latest_time_prioq_compare);
+ if (r < 0)
+ return r;
if (d->fd < 0) {
r = event_setup_timer_fd(e, d, clock);
@@ -1153,8 +1153,8 @@ _public_ int sd_event_add_signal(
callback = signal_exit_callback;
r = pthread_sigmask(SIG_SETMASK, NULL, &ss);
- if (r < 0)
- return -errno;
+ if (r != 0)
+ return -r;
if (!sigismember(&ss, sig))
return -EBUSY;
@@ -1215,7 +1215,7 @@ _public_ int sd_event_add_child(
if (r < 0)
return r;
- if (hashmap_contains(e->child_sources, INT_TO_PTR(pid)))
+ if (hashmap_contains(e->child_sources, PID_TO_PTR(pid)))
return -EBUSY;
s = source_new(e, !ret, SOURCE_CHILD);
@@ -1228,7 +1228,7 @@ _public_ int sd_event_add_child(
s->userdata = userdata;
s->enabled = SD_EVENT_ONESHOT;
- r = hashmap_put(e->child_sources, INT_TO_PTR(pid), s);
+ r = hashmap_put(e->child_sources, PID_TO_PTR(pid), s);
if (r < 0) {
source_free(s);
return r;
@@ -1337,11 +1337,9 @@ _public_ int sd_event_add_exit(
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(!event_pid_changed(e), -ECHILD);
- if (!e->exit) {
- e->exit = prioq_new(exit_prioq_compare);
- if (!e->exit)
- return -ENOMEM;
- }
+ r = prioq_ensure_allocated(&e->exit, exit_prioq_compare);
+ if (r < 0)
+ return r;
s = source_new(e, !ret, SOURCE_EXIT);
if (!s)
@@ -1365,7 +1363,9 @@ _public_ int sd_event_add_exit(
}
_public_ sd_event_source* sd_event_source_ref(sd_event_source *s) {
- assert_return(s, NULL);
+
+ if (!s)
+ return NULL;
assert(s->n_ref >= 1);
s->n_ref++;
@@ -2311,12 +2311,9 @@ static int source_dispatch(sd_event_source *s) {
s->dispatching = false;
- if (r < 0) {
- if (s->description)
- log_debug_errno(r, "Event source '%s' returned error, disabling: %m", s->description);
- else
- log_debug_errno(r, "Event source %p returned error, disabling: %m", s);
- }
+ if (r < 0)
+ log_debug_errno(r, "Event source %s (type %s) returned error, disabling: %m",
+ strna(s->description), event_source_type_to_string(s->type));
if (s->n_ref == 0)
source_free(s);
@@ -2349,12 +2346,9 @@ static int event_prepare(sd_event *e) {
r = s->prepare(s, s->userdata);
s->dispatching = false;
- if (r < 0) {
- if (s->description)
- log_debug_errno(r, "Prepare callback of event source '%s' returned error, disabling: %m", s->description);
- else
- log_debug_errno(r, "Prepare callback of event source %p returned error, disabling: %m", s);
- }
+ if (r < 0)
+ log_debug_errno(r, "Prepare callback of event source %s (type %s) returned error, disabling: %m",
+ strna(s->description), event_source_type_to_string(s->type));
if (s->n_ref == 0)
source_free(s);
@@ -2459,7 +2453,9 @@ _public_ int sd_event_prepare(sd_event *e) {
e->iteration++;
+ e->state = SD_EVENT_PREPARING;
r = event_prepare(e);
+ e->state = SD_EVENT_INITIAL;
if (r < 0)
return r;
@@ -2637,6 +2633,18 @@ _public_ int sd_event_dispatch(sd_event *e) {
return 1;
}
+static void event_log_delays(sd_event *e) {
+ char b[ELEMENTSOF(e->delays) * DECIMAL_STR_MAX(unsigned) + 1];
+ unsigned i;
+ int o;
+
+ for (i = o = 0; i < ELEMENTSOF(e->delays); i++) {
+ o += snprintf(&b[o], sizeof(b) - o, "%u ", e->delays[i]);
+ e->delays[i] = 0;
+ }
+ log_debug("Event loop iterations: %.*s", o, b);
+}
+
_public_ int sd_event_run(sd_event *e, uint64_t timeout) {
int r;
@@ -2645,11 +2653,30 @@ _public_ int sd_event_run(sd_event *e, uint64_t timeout) {
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
+ if (e->profile_delays && e->last_run) {
+ usec_t this_run;
+ unsigned l;
+
+ this_run = now(CLOCK_MONOTONIC);
+
+ l = u64log2(this_run - e->last_run);
+ assert(l < sizeof(e->delays));
+ e->delays[l]++;
+
+ if (this_run - e->last_log >= 5*USEC_PER_SEC) {
+ event_log_delays(e);
+ e->last_log = this_run;
+ }
+ }
+
r = sd_event_prepare(e);
if (r == 0)
/* There was nothing? Then wait... */
r = sd_event_wait(e, timeout);
+ if (e->profile_delays)
+ e->last_run = now(CLOCK_MONOTONIC);
+
if (r > 0) {
/* There's something now, then let's dispatch it */
r = sd_event_dispatch(e);
@@ -2726,6 +2753,12 @@ _public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) {
assert_return(e, -EINVAL);
assert_return(usec, -EINVAL);
assert_return(!event_pid_changed(e), -ECHILD);
+ assert_return(IN_SET(clock,
+ CLOCK_REALTIME,
+ CLOCK_REALTIME_ALARM,
+ CLOCK_MONOTONIC,
+ CLOCK_BOOTTIME,
+ CLOCK_BOOTTIME_ALARM), -EOPNOTSUPP);
if (!dual_timestamp_is_set(&e->timestamp)) {
/* Implicitly fall back to now() if we never ran
@@ -2745,8 +2778,7 @@ _public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) {
*usec = e->timestamp.monotonic;
break;
- case CLOCK_BOOTTIME:
- case CLOCK_BOOTTIME_ALARM:
+ default:
*usec = e->timestamp_boottime;
break;
}
diff --git a/src/libsystemd/sd-event/test-event.c b/src/libsystemd/sd-event/test-event.c
index c092e56b7a..c605b18ae9 100644
--- a/src/libsystemd/sd-event/test-event.c
+++ b/src/libsystemd/sd-event/test-event.c
@@ -20,10 +20,12 @@
***/
#include "sd-event.h"
+
+#include "fd-util.h"
#include "log.h"
-#include "util.h"
#include "macro.h"
#include "signal-util.h"
+#include "util.h"
static int prepare_handler(sd_event_source *s, void *userdata) {
log_info("preparing %c", PTR_TO_INT(userdata));
@@ -156,11 +158,22 @@ static int exit_handler(sd_event_source *s, void *userdata) {
return 3;
}
+static bool got_post = false;
+
+static int post_handler(sd_event_source *s, void *userdata) {
+ log_info("got post handler");
+
+ got_post = true;
+
+ return 2;
+}
+
static void test_basic(void) {
sd_event *e = NULL;
sd_event_source *w = NULL, *x = NULL, *y = NULL, *z = NULL, *q = NULL, *t = NULL;
static const char ch = 'x';
int a[2] = { -1, -1 }, b[2] = { -1, -1}, d[2] = { -1, -1}, k[2] = { -1, -1 };
+ uint64_t event_now;
assert_se(pipe(a) >= 0);
assert_se(pipe(b) >= 0);
@@ -168,6 +181,7 @@ static void test_basic(void) {
assert_se(pipe(k) >= 0);
assert_se(sd_event_default(&e) >= 0);
+ assert_se(sd_event_now(e, CLOCK_MONOTONIC, &event_now) > 0);
assert_se(sd_event_set_watchdog(e, true) >= 0);
@@ -228,10 +242,14 @@ static void test_basic(void) {
sd_event_source_unref(y);
do_quit = true;
- assert_se(sd_event_source_set_time(z, now(CLOCK_MONOTONIC) + 200 * USEC_PER_MSEC) >= 0);
+ assert_se(sd_event_add_post(e, NULL, post_handler, NULL) >= 0);
+ assert_se(sd_event_now(e, CLOCK_MONOTONIC, &event_now) == 0);
+ assert_se(sd_event_source_set_time(z, event_now + 200 * USEC_PER_MSEC) >= 0);
assert_se(sd_event_source_set_enabled(z, SD_EVENT_ONESHOT) >= 0);
assert_se(sd_event_loop(e) >= 0);
+ assert_se(got_post);
+ assert_se(got_exit);
sd_event_source_unref(z);
sd_event_source_unref(q);
@@ -246,6 +264,30 @@ static void test_basic(void) {
safe_close_pair(k);
}
+static void test_sd_event_now(void) {
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ uint64_t event_now;
+
+ assert_se(sd_event_new(&e) >= 0);
+ assert_se(sd_event_now(e, CLOCK_MONOTONIC, &event_now) > 0);
+ assert_se(sd_event_now(e, CLOCK_REALTIME, &event_now) > 0);
+ assert_se(sd_event_now(e, CLOCK_REALTIME_ALARM, &event_now) > 0);
+ assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) > 0);
+ assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) > 0);
+ assert_se(sd_event_now(e, -1, &event_now) == -EOPNOTSUPP);
+ assert_se(sd_event_now(e, 900 /* arbitrary big number */, &event_now) == -EOPNOTSUPP);
+
+ assert_se(sd_event_run(e, 0) == 0);
+
+ assert_se(sd_event_now(e, CLOCK_MONOTONIC, &event_now) == 0);
+ assert_se(sd_event_now(e, CLOCK_REALTIME, &event_now) == 0);
+ assert_se(sd_event_now(e, CLOCK_REALTIME_ALARM, &event_now) == 0);
+ assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) == 0);
+ assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) == 0);
+ assert_se(sd_event_now(e, -1, &event_now) == -EOPNOTSUPP);
+ assert_se(sd_event_now(e, 900 /* arbitrary big number */, &event_now) == -EOPNOTSUPP);
+}
+
static int last_rtqueue_sigval = 0;
static int n_rtqueue = 0;
@@ -306,7 +348,11 @@ static void test_rtqueue(void) {
int main(int argc, char *argv[]) {
+ log_set_max_level(LOG_DEBUG);
+ log_parse_environment();
+
test_basic();
+ test_sd_event_now();
test_rtqueue();
return 0;
diff --git a/src/libsystemd/sd-hwdb/hwdb-internal.h b/src/libsystemd/sd-hwdb/hwdb-internal.h
index fedccdec72..13fddfc8ad 100644
--- a/src/libsystemd/sd-hwdb/hwdb-internal.h
+++ b/src/libsystemd/sd-hwdb/hwdb-internal.h
@@ -19,6 +19,7 @@
#pragma once
#include "sparse-endian.h"
+#include "util.h"
#define HWDB_SIG { 'K', 'S', 'L', 'P', 'H', 'H', 'R', 'H' }
diff --git a/src/libsystemd/sd-hwdb/hwdb-util.h b/src/libsystemd/sd-hwdb/hwdb-util.h
index ee020a2942..6765ba9400 100644
--- a/src/libsystemd/sd-hwdb/hwdb-util.h
+++ b/src/libsystemd/sd-hwdb/hwdb-util.h
@@ -21,11 +21,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
-
#include "sd-hwdb.h"
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_hwdb*, sd_hwdb_unref);
-#define _cleanup_hwdb_unref_ _cleanup_(sd_hwdb_unrefp)
+#include "util.h"
bool hwdb_validate(sd_hwdb *hwdb);
diff --git a/src/libsystemd/sd-hwdb/sd-hwdb.c b/src/libsystemd/sd-hwdb/sd-hwdb.c
index f0316be659..062fa97b17 100644
--- a/src/libsystemd/sd-hwdb/sd-hwdb.c
+++ b/src/libsystemd/sd-hwdb/sd-hwdb.c
@@ -19,21 +19,23 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
#include <errno.h>
-#include <string.h>
+#include <fnmatch.h>
#include <inttypes.h>
+#include <stdio.h>
#include <stdlib.h>
-#include <fnmatch.h>
+#include <string.h>
#include <sys/mman.h>
#include "sd-hwdb.h"
+#include "alloc-util.h"
+#include "fd-util.h"
#include "hashmap.h"
-#include "refcnt.h"
-
-#include "hwdb-util.h"
#include "hwdb-internal.h"
+#include "hwdb-util.h"
+#include "refcnt.h"
+#include "string-util.h"
struct sd_hwdb {
RefCount n_ref;
@@ -277,7 +279,7 @@ static const char hwdb_bin_paths[] =
UDEVLIBEXECDIR "/hwdb.bin\0";
_public_ int sd_hwdb_new(sd_hwdb **ret) {
- _cleanup_hwdb_unref_ sd_hwdb *hwdb = NULL;
+ _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
const char *hwdb_bin_path;
const char sig[] = HWDB_SIG;
diff --git a/src/libsystemd/sd-id128/sd-id128.c b/src/libsystemd/sd-id128/sd-id128.c
index eb539ad318..c12bb1e20b 100644
--- a/src/libsystemd/sd-id128/sd-id128.c
+++ b/src/libsystemd/sd-id128/sd-id128.c
@@ -23,10 +23,14 @@
#include <fcntl.h>
#include <unistd.h>
-#include "util.h"
-#include "macro.h"
#include "sd-id128.h"
+
+#include "fd-util.h"
+#include "hexdecoct.h"
+#include "io-util.h"
+#include "macro.h"
#include "random-util.h"
+#include "util.h"
_public_ char *sd_id128_to_string(sd_id128_t id, char s[SD_ID128_STRING_MAX]) {
unsigned n;
diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c
index 265c7c7db2..ef240c3531 100644
--- a/src/libsystemd/sd-login/sd-login.c
+++ b/src/libsystemd/sd-login/sd-login.c
@@ -19,21 +19,33 @@
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 <string.h>
+#include <sys/inotify.h>
+#include <unistd.h>
-#include "util.h"
+#include "sd-login.h"
+
+#include "alloc-util.h"
#include "cgroup-util.h"
-#include "macro.h"
-#include "strv.h"
+#include "dirent-util.h"
+#include "escape.h"
+#include "fd-util.h"
#include "fileio.h"
-#include "login-util.h"
#include "formats-util.h"
+#include "fs-util.h"
#include "hostname-util.h"
-#include "sd-login.h"
+#include "io-util.h"
+#include "login-util.h"
+#include "macro.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "socket-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "user-util.h"
+#include "util.h"
/* Error codes:
*
@@ -798,7 +810,7 @@ _public_ int sd_get_uids(uid_t **users) {
errno = 0;
de = readdir(d);
- if (!de && errno != 0)
+ if (!de && errno > 0)
return -errno;
if (!de)
@@ -920,9 +932,7 @@ _public_ int sd_machine_get_ifindices(const char *machine, int **ifindices) {
*(char*) (mempcpy(buf, word, l)) = 0;
- if (safe_atoi(buf, &ifi) < 0)
- continue;
- if (ifi <= 0)
+ if (parse_ifindex(buf, &ifi) < 0)
continue;
if (!GREEDY_REALLOC(ni, allocated, nr+1)) {
@@ -1007,7 +1017,8 @@ _public_ int sd_login_monitor_new(const char *category, sd_login_monitor **m) {
_public_ sd_login_monitor* sd_login_monitor_unref(sd_login_monitor *m) {
int fd;
- assert_return(m, NULL);
+ if (!m)
+ return NULL;
fd = MONITOR_TO_FD(m);
close_nointr(fd);
diff --git a/src/libsystemd/sd-login/test-login.c b/src/libsystemd/sd-login/test-login.c
index f734ce9eee..b0f94c9522 100644
--- a/src/libsystemd/sd-login/test-login.c
+++ b/src/libsystemd/sd-login/test-login.c
@@ -22,11 +22,14 @@
#include <poll.h>
#include <string.h>
-#include "systemd/sd-login.h"
+#include "sd-login.h"
-#include "util.h"
-#include "strv.h"
+#include "alloc-util.h"
+#include "fd-util.h"
#include "formats-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "util.h"
static void test_login(void) {
_cleanup_close_pair_ int pair[2] = { -1, -1 };
diff --git a/src/libsystemd/sd-netlink/local-addresses.c b/src/libsystemd/sd-netlink/local-addresses.c
index e2f637f7f9..997b048e58 100644
--- a/src/libsystemd/sd-netlink/local-addresses.c
+++ b/src/libsystemd/sd-netlink/local-addresses.c
@@ -21,9 +21,11 @@
***/
#include "sd-netlink.h"
-#include "netlink-util.h"
-#include "macro.h"
+
+#include "alloc-util.h"
#include "local-addresses.h"
+#include "macro.h"
+#include "netlink-util.h"
static int address_compare(const void *_a, const void *_b) {
const struct local_address *a = _a, *b = _b;
@@ -54,8 +56,8 @@ static int address_compare(const void *_a, const void *_b) {
}
int local_addresses(sd_netlink *context, int ifindex, int af, struct local_address **ret) {
- _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
- _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
_cleanup_free_ struct local_address *list = NULL;
size_t n_list = 0, n_allocated = 0;
sd_netlink_message *m;
@@ -165,8 +167,8 @@ int local_addresses(sd_netlink *context, int ifindex, int af, struct local_addre
}
int local_gateways(sd_netlink *context, int ifindex, int af, struct local_address **ret) {
- _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
- _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
_cleanup_free_ struct local_address *list = NULL;
sd_netlink_message *m = NULL;
size_t n_list = 0, n_allocated = 0;
diff --git a/src/libsystemd/sd-netlink/local-addresses.h b/src/libsystemd/sd-netlink/local-addresses.h
index 5d0f11a2c1..74d4f25534 100644
--- a/src/libsystemd/sd-netlink/local-addresses.h
+++ b/src/libsystemd/sd-netlink/local-addresses.h
@@ -23,6 +23,7 @@
#include "sd-netlink.h"
+
#include "in-addr-util.h"
struct local_address {
diff --git a/src/libsystemd/sd-netlink/netlink-internal.h b/src/libsystemd/sd-netlink/netlink-internal.h
index 4026e2c341..6c752ac65e 100644
--- a/src/libsystemd/sd-netlink/netlink-internal.h
+++ b/src/libsystemd/sd-netlink/netlink-internal.h
@@ -23,13 +23,12 @@
#include <linux/netlink.h>
-#include "refcnt.h"
-#include "prioq.h"
-#include "list.h"
-
#include "sd-netlink.h"
+#include "list.h"
#include "netlink-types.h"
+#include "prioq.h"
+#include "refcnt.h"
#define RTNL_DEFAULT_TIMEOUT ((usec_t) (25 * USEC_PER_SEC))
@@ -64,6 +63,9 @@ struct sd_netlink {
struct sockaddr_nl nl;
} sockaddr;
+ Hashmap *broadcast_group_refs;
+ bool broadcast_group_dont_leave:1; /* until we can rely on 4.2 */
+
sd_netlink_message **rqueue;
unsigned rqueue_size;
size_t rqueue_allocated;
@@ -124,7 +126,8 @@ int message_new_empty(sd_netlink *rtnl, sd_netlink_message **ret);
int socket_open(int family);
int socket_bind(sd_netlink *nl);
-int socket_join_broadcast_group(sd_netlink *nl, unsigned group);
+int socket_broadcast_group_ref(sd_netlink *nl, unsigned group);
+int socket_broadcast_group_unref(sd_netlink *nl, unsigned group);
int socket_write_message(sd_netlink *nl, sd_netlink_message *m);
int socket_read_message(sd_netlink *nl);
@@ -132,5 +135,5 @@ int rtnl_rqueue_make_room(sd_netlink *rtnl);
int rtnl_rqueue_partial_make_room(sd_netlink *rtnl);
/* Make sure callbacks don't destroy the rtnl connection */
-#define RTNL_DONT_DESTROY(rtnl) \
- _cleanup_netlink_unref_ _unused_ sd_netlink *_dont_destroy_##rtnl = sd_netlink_ref(rtnl)
+#define NETLINK_DONT_DESTROY(rtnl) \
+ _cleanup_(sd_netlink_unrefp) _unused_ sd_netlink *_dont_destroy_##rtnl = sd_netlink_ref(rtnl)
diff --git a/src/libsystemd/sd-netlink/netlink-message.c b/src/libsystemd/sd-netlink/netlink-message.c
index 0d8e37b856..50792bc4a3 100644
--- a/src/libsystemd/sd-netlink/netlink-message.c
+++ b/src/libsystemd/sd-netlink/netlink-message.c
@@ -23,16 +23,17 @@
#include <stdbool.h>
#include <unistd.h>
-#include "util.h"
-#include "socket-util.h"
+#include "sd-netlink.h"
+
+#include "alloc-util.h"
#include "formats-util.h"
-#include "refcnt.h"
#include "missing.h"
-
-#include "sd-netlink.h"
-#include "netlink-util.h"
#include "netlink-internal.h"
#include "netlink-types.h"
+#include "netlink-util.h"
+#include "refcnt.h"
+#include "socket-util.h"
+#include "util.h"
#define GET_CONTAINER(m, i) ((i) < (m)->n_containers ? (struct rtattr*)((uint8_t*)(m)->hdr + (m)->containers[i].offset) : NULL)
#define PUSH_CONTAINER(m, new) (m)->container_offsets[(m)->n_containers ++] = (uint8_t*)(new) - (uint8_t*)(m)->hdr;
@@ -64,7 +65,7 @@ int message_new_empty(sd_netlink *rtnl, sd_netlink_message **ret) {
}
int message_new(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t type) {
- _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
const NLType *nl_type;
size_t size;
int r;
@@ -149,6 +150,15 @@ int sd_netlink_message_get_type(sd_netlink_message *m, uint16_t *type) {
return 0;
}
+int sd_netlink_message_set_flags(sd_netlink_message *m, uint16_t flags) {
+ assert_return(m, -EINVAL);
+ assert_return(flags, -EINVAL);
+
+ m->hdr->nlmsg_flags = flags;
+
+ return 0;
+}
+
int sd_netlink_message_is_broadcast(sd_netlink_message *m) {
assert_return(m, -EINVAL);
diff --git a/src/libsystemd/sd-netlink/netlink-socket.c b/src/libsystemd/sd-netlink/netlink-socket.c
index 84ff7c38c9..e95c99af0d 100644
--- a/src/libsystemd/sd-netlink/netlink-socket.c
+++ b/src/libsystemd/sd-netlink/netlink-socket.c
@@ -23,16 +23,17 @@
#include <stdbool.h>
#include <unistd.h>
-#include "util.h"
-#include "socket-util.h"
+#include "sd-netlink.h"
+
+#include "alloc-util.h"
#include "formats-util.h"
-#include "refcnt.h"
#include "missing.h"
-
-#include "sd-netlink.h"
-#include "netlink-util.h"
#include "netlink-internal.h"
#include "netlink-types.h"
+#include "netlink-util.h"
+#include "refcnt.h"
+#include "socket-util.h"
+#include "util.h"
int socket_open(int family) {
int fd;
@@ -44,6 +45,65 @@ int socket_open(int family) {
return fd;
}
+static int broadcast_groups_get(sd_netlink *nl) {
+ _cleanup_free_ uint32_t *groups = NULL;
+ socklen_t len = 0, old_len;
+ unsigned i, j;
+ int r;
+
+ assert(nl);
+ assert(nl->fd >= 0);
+
+ r = getsockopt(nl->fd, SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS, NULL, &len);
+ if (r < 0) {
+ if (errno == ENOPROTOOPT) {
+ nl->broadcast_group_dont_leave = true;
+ return 0;
+ } else
+ return -errno;
+ }
+
+ if (len == 0)
+ return 0;
+
+ groups = new0(uint32_t, len);
+ if (!groups)
+ return -ENOMEM;
+
+ old_len = len;
+
+ r = getsockopt(nl->fd, SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS, groups, &len);
+ if (r < 0)
+ return -errno;
+
+ if (old_len != len)
+ return -EIO;
+
+ r = hashmap_ensure_allocated(&nl->broadcast_group_refs, NULL);
+ if (r < 0)
+ return r;
+
+ for (i = 0; i < len; i++) {
+ for (j = 0; j < sizeof(uint32_t) * 8; j ++) {
+ uint32_t offset;
+ unsigned group;
+
+ offset = 1U << j;
+
+ if (!(groups[i] & offset))
+ continue;
+
+ group = i * sizeof(uint32_t) * 8 + j + 1;
+
+ r = hashmap_put(nl->broadcast_group_refs, UINT_TO_PTR(group), UINT_TO_PTR(1));
+ if (r < 0)
+ return r;
+ }
+ }
+
+ return 0;
+}
+
int socket_bind(sd_netlink *nl) {
socklen_t addrlen;
int r, one = 1;
@@ -63,11 +123,32 @@ int socket_bind(sd_netlink *nl) {
if (r < 0)
return -errno;
+ r = broadcast_groups_get(nl);
+ if (r < 0)
+ return r;
+
return 0;
}
+static unsigned broadcast_group_get_ref(sd_netlink *nl, unsigned group) {
+ assert(nl);
+
+ return PTR_TO_UINT(hashmap_get(nl->broadcast_group_refs, UINT_TO_PTR(group)));
+}
+
+static int broadcast_group_set_ref(sd_netlink *nl, unsigned group, unsigned n_ref) {
+ int r;
+
+ assert(nl);
+
+ r = hashmap_replace(nl->broadcast_group_refs, UINT_TO_PTR(group), UINT_TO_PTR(n_ref));
+ if (r < 0)
+ return r;
+
+ return 0;
+}
-int socket_join_broadcast_group(sd_netlink *nl, unsigned group) {
+static int broadcast_group_join(sd_netlink *nl, unsigned group) {
int r;
assert(nl);
@@ -81,6 +162,79 @@ int socket_join_broadcast_group(sd_netlink *nl, unsigned group) {
return 0;
}
+int socket_broadcast_group_ref(sd_netlink *nl, unsigned group) {
+ unsigned n_ref;
+ int r;
+
+ assert(nl);
+
+ n_ref = broadcast_group_get_ref(nl, group);
+
+ n_ref ++;
+
+ r = hashmap_ensure_allocated(&nl->broadcast_group_refs, NULL);
+ if (r < 0)
+ return r;
+
+ r = broadcast_group_set_ref(nl, group, n_ref);
+ if (r < 0)
+ return r;
+
+ if (n_ref > 1)
+ /* not yet in the group */
+ return 0;
+
+ r = broadcast_group_join(nl, group);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int broadcast_group_leave(sd_netlink *nl, unsigned group) {
+ int r;
+
+ assert(nl);
+ assert(nl->fd >= 0);
+ assert(group > 0);
+
+ if (nl->broadcast_group_dont_leave)
+ return 0;
+
+ r = setsockopt(nl->fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP, &group, sizeof(group));
+ if (r < 0)
+ return -errno;
+
+ return 0;
+}
+
+int socket_broadcast_group_unref(sd_netlink *nl, unsigned group) {
+ unsigned n_ref;
+ int r;
+
+ assert(nl);
+
+ n_ref = broadcast_group_get_ref(nl, group);
+
+ assert(n_ref > 0);
+
+ n_ref --;
+
+ r = broadcast_group_set_ref(nl, group, n_ref);
+ if (r < 0)
+ return r;
+
+ if (n_ref > 0)
+ /* still refs left */
+ return 0;
+
+ r = broadcast_group_leave(nl, group);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
/* returns the number of bytes sent, or a negative error code */
int socket_write_message(sd_netlink *nl, sd_netlink_message *m) {
union {
@@ -169,7 +323,7 @@ static int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool
* On failure, a negative error code is returned.
*/
int socket_read_message(sd_netlink *rtnl) {
- _cleanup_netlink_message_unref_ sd_netlink_message *first = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *first = NULL;
struct iovec iov = {};
uint32_t group = 0;
bool multi_part = false, done = false;
@@ -222,7 +376,7 @@ int socket_read_message(sd_netlink *rtnl) {
}
for (new_msg = rtnl->rbuffer; NLMSG_OK(new_msg, len) && !done; new_msg = NLMSG_NEXT(new_msg, len)) {
- _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
const NLType *nl_type;
if (!group && new_msg->nlmsg_pid != rtnl->sockaddr.nl.nl_pid)
diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c
index 2128329191..be4ab1373d 100644
--- a/src/libsystemd/sd-netlink/netlink-types.c
+++ b/src/libsystemd/sd-netlink/netlink-types.c
@@ -28,16 +28,15 @@
#include <linux/if_bridge.h>
#include <linux/if_addr.h>
#include <linux/if.h>
-
#include <linux/ip.h>
#include <linux/if_link.h>
#include <linux/if_tunnel.h>
#include "macro.h"
-#include "util.h"
-
-#include "netlink-types.h"
#include "missing.h"
+#include "netlink-types.h"
+#include "string-table.h"
+#include "util.h"
/* Maximum ARP IP target defined in kernel */
#define BOND_MAX_ARP_TARGETS 16
@@ -84,29 +83,29 @@ static const NLTypeSystem empty_type_system = {
.types = empty_types,
};
-static const NLType rtnl_link_info_data_veth_types[VETH_INFO_MAX + 1] = {
+static const NLType rtnl_link_info_data_veth_types[] = {
[VETH_INFO_PEER] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
};
-static const NLType rtnl_link_info_data_ipvlan_types[IFLA_IPVLAN_MAX + 1] = {
+static const NLType rtnl_link_info_data_ipvlan_types[] = {
[IFLA_IPVLAN_MODE] = { .type = NETLINK_TYPE_U16 },
};
-static const NLType rtnl_link_info_data_macvlan_types[IFLA_MACVLAN_MAX + 1] = {
+static const NLType rtnl_link_info_data_macvlan_types[] = {
[IFLA_MACVLAN_MODE] = { .type = NETLINK_TYPE_U32 },
[IFLA_MACVLAN_FLAGS] = { .type = NETLINK_TYPE_U16 },
};
-static const NLType rtnl_link_info_data_bridge_types[IFLA_BRIDGE_MAX + 1] = {
- [IFLA_BRIDGE_FLAGS] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BRIDGE_MODE] = { .type = NETLINK_TYPE_U16 },
-/*
- [IFLA_BRIDGE_VLAN_INFO] = { .type = NETLINK_TYPE_BINARY,
- .len = sizeof(struct bridge_vlan_info), },
-*/
+static const NLType rtnl_link_info_data_bridge_types[] = {
+ [IFLA_BR_FORWARD_DELAY] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BR_HELLO_TIME] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BR_MAX_AGE] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BR_AGEING_TIME] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BR_STP_STATE] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BR_PRIORITY] = { .type = NETLINK_TYPE_U16 },
};
-static const NLType rtnl_link_info_data_vlan_types[IFLA_VLAN_MAX + 1] = {
+static const NLType rtnl_link_info_data_vlan_types[] = {
[IFLA_VLAN_ID] = { .type = NETLINK_TYPE_U16 },
/*
[IFLA_VLAN_FLAGS] = { .len = sizeof(struct ifla_vlan_flags) },
@@ -116,7 +115,7 @@ static const NLType rtnl_link_info_data_vlan_types[IFLA_VLAN_MAX + 1] = {
[IFLA_VLAN_PROTOCOL] = { .type = NETLINK_TYPE_U16 },
};
-static const NLType rtnl_link_info_data_vxlan_types[IFLA_VXLAN_MAX+1] = {
+static const NLType rtnl_link_info_data_vxlan_types[] = {
[IFLA_VXLAN_ID] = { .type = NETLINK_TYPE_U32 },
[IFLA_VXLAN_GROUP] = { .type = NETLINK_TYPE_IN_ADDR },
[IFLA_VXLAN_LINK] = { .type = NETLINK_TYPE_U32 },
@@ -143,7 +142,7 @@ static const NLType rtnl_link_info_data_vxlan_types[IFLA_VXLAN_MAX+1] = {
[IFLA_VXLAN_REMCSUM_NOPARTIAL] = { .type = NETLINK_TYPE_FLAG },
};
-static const NLType rtnl_bond_arp_target_types[BOND_ARP_TARGETS_MAX + 1] = {
+static const NLType rtnl_bond_arp_target_types[] = {
[BOND_ARP_TARGETS_0] = { .type = NETLINK_TYPE_U32 },
[BOND_ARP_TARGETS_1] = { .type = NETLINK_TYPE_U32 },
[BOND_ARP_TARGETS_2] = { .type = NETLINK_TYPE_U32 },
@@ -167,7 +166,7 @@ static const NLTypeSystem rtnl_bond_arp_type_system = {
.types = rtnl_bond_arp_target_types,
};
-static const NLType rtnl_link_info_data_bond_types[IFLA_BOND_MAX + 1] = {
+static const NLType rtnl_link_info_data_bond_types[] = {
[IFLA_BOND_MODE] = { .type = NETLINK_TYPE_U8 },
[IFLA_BOND_ACTIVE_SLAVE] = { .type = NETLINK_TYPE_U32 },
[IFLA_BOND_MIIMON] = { .type = NETLINK_TYPE_U32 },
@@ -193,7 +192,7 @@ static const NLType rtnl_link_info_data_bond_types[IFLA_BOND_MAX + 1] = {
[IFLA_BOND_AD_INFO] = { .type = NETLINK_TYPE_NESTED },
};
-static const NLType rtnl_link_info_data_iptun_types[IFLA_IPTUN_MAX + 1] = {
+static const NLType rtnl_link_info_data_iptun_types[] = {
[IFLA_IPTUN_LINK] = { .type = NETLINK_TYPE_U32 },
[IFLA_IPTUN_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR },
[IFLA_IPTUN_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR },
@@ -212,7 +211,7 @@ static const NLType rtnl_link_info_data_iptun_types[IFLA_IPTUN_MAX + 1] = {
[IFLA_IPTUN_ENCAP_DPORT] = { .type = NETLINK_TYPE_U16 },
};
-static const NLType rtnl_link_info_data_ipgre_types[IFLA_GRE_MAX + 1] = {
+static const NLType rtnl_link_info_data_ipgre_types[] = {
[IFLA_GRE_LINK] = { .type = NETLINK_TYPE_U32 },
[IFLA_GRE_IFLAGS] = { .type = NETLINK_TYPE_U16 },
[IFLA_GRE_OFLAGS] = { .type = NETLINK_TYPE_U16 },
@@ -231,7 +230,7 @@ static const NLType rtnl_link_info_data_ipgre_types[IFLA_GRE_MAX + 1] = {
[IFLA_GRE_ENCAP_DPORT] = { .type = NETLINK_TYPE_U16 },
};
-static const NLType rtnl_link_info_data_ipvti_types[IFLA_VTI_MAX + 1] = {
+static const NLType rtnl_link_info_data_ipvti_types[] = {
[IFLA_VTI_LINK] = { .type = NETLINK_TYPE_U32 },
[IFLA_VTI_IKEY] = { .type = NETLINK_TYPE_U32 },
[IFLA_VTI_OKEY] = { .type = NETLINK_TYPE_U32 },
@@ -239,7 +238,7 @@ static const NLType rtnl_link_info_data_ipvti_types[IFLA_VTI_MAX + 1] = {
[IFLA_VTI_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR },
};
-static const NLType rtnl_link_info_data_ip6tnl_types[IFLA_IPTUN_MAX + 1] = {
+static const NLType rtnl_link_info_data_ip6tnl_types[] = {
[IFLA_IPTUN_LINK] = { .type = NETLINK_TYPE_U32 },
[IFLA_IPTUN_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR },
[IFLA_IPTUN_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR },
@@ -251,7 +250,7 @@ static const NLType rtnl_link_info_data_ip6tnl_types[IFLA_IPTUN_MAX + 1] = {
};
/* these strings must match the .kind entries in the kernel */
-static const char* const nl_union_link_info_data_table[_NL_UNION_LINK_INFO_DATA_MAX] = {
+static const char* const nl_union_link_info_data_table[] = {
[NL_UNION_LINK_INFO_DATA_BOND] = "bond",
[NL_UNION_LINK_INFO_DATA_BRIDGE] = "bridge",
[NL_UNION_LINK_INFO_DATA_VLAN] = "vlan",
@@ -274,7 +273,7 @@ static const char* const nl_union_link_info_data_table[_NL_UNION_LINK_INFO_DATA_
DEFINE_STRING_TABLE_LOOKUP(nl_union_link_info_data, NLUnionLinkInfoData);
-static const NLTypeSystem rtnl_link_info_data_type_systems[_NL_UNION_LINK_INFO_DATA_MAX] = {
+static const NLTypeSystem rtnl_link_info_data_type_systems[] = {
[NL_UNION_LINK_INFO_DATA_BOND] = { .count = ELEMENTSOF(rtnl_link_info_data_bond_types),
.types = rtnl_link_info_data_bond_types },
[NL_UNION_LINK_INFO_DATA_BRIDGE] = { .count = ELEMENTSOF(rtnl_link_info_data_bridge_types),
@@ -320,7 +319,7 @@ static const NLTypeSystemUnion rtnl_link_info_data_type_system_union = {
.match = IFLA_INFO_KIND,
};
-static const NLType rtnl_link_info_types[IFLA_INFO_MAX + 1] = {
+static const NLType rtnl_link_info_types[] = {
[IFLA_INFO_KIND] = { .type = NETLINK_TYPE_STRING },
[IFLA_INFO_DATA] = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_link_info_data_type_system_union},
/*
@@ -335,7 +334,7 @@ static const NLTypeSystem rtnl_link_info_type_system = {
.types = rtnl_link_info_types,
};
-static const struct NLType rtnl_prot_info_bridge_port_types[IFLA_BRPORT_MAX + 1] = {
+static const struct NLType rtnl_prot_info_bridge_port_types[] = {
[IFLA_BRPORT_STATE] = { .type = NETLINK_TYPE_U8 },
[IFLA_BRPORT_COST] = { .type = NETLINK_TYPE_U32 },
[IFLA_BRPORT_PRIORITY] = { .type = NETLINK_TYPE_U16 },
@@ -349,7 +348,7 @@ static const struct NLType rtnl_prot_info_bridge_port_types[IFLA_BRPORT_MAX + 1]
[IFLA_BRPORT_LEARNING_SYNC] = { .type = NETLINK_TYPE_U8 },
};
-static const NLTypeSystem rtnl_prot_info_type_systems[AF_MAX] = {
+static const NLTypeSystem rtnl_prot_info_type_systems[] = {
[AF_BRIDGE] = { .count = ELEMENTSOF(rtnl_prot_info_bridge_port_types),
.types = rtnl_prot_info_bridge_port_types },
};
@@ -360,7 +359,7 @@ static const NLTypeSystemUnion rtnl_prot_info_type_system_union = {
.match_type = NL_MATCH_PROTOCOL,
};
-static const struct NLType rtnl_af_spec_inet6_types[IFLA_INET6_MAX + 1] = {
+static const struct NLType rtnl_af_spec_inet6_types[] = {
[IFLA_INET6_FLAGS] = { .type = NETLINK_TYPE_U32 },
/*
IFLA_INET6_CONF,
@@ -378,7 +377,7 @@ static const NLTypeSystem rtnl_af_spec_inet6_type_system = {
.types = rtnl_af_spec_inet6_types,
};
-static const NLType rtnl_af_spec_types[AF_MAX + 1] = {
+static const NLType rtnl_af_spec_types[] = {
[AF_INET6] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_af_spec_inet6_type_system },
};
@@ -387,7 +386,7 @@ static const NLTypeSystem rtnl_af_spec_type_system = {
.types = rtnl_af_spec_types,
};
-static const NLType rtnl_link_types[IFLA_MAX + 1 ] = {
+static const NLType rtnl_link_types[] = {
[IFLA_ADDRESS] = { .type = NETLINK_TYPE_ETHER_ADDR },
[IFLA_BROADCAST] = { .type = NETLINK_TYPE_ETHER_ADDR },
[IFLA_IFNAME] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 },
@@ -446,7 +445,7 @@ static const NLTypeSystem rtnl_link_type_system = {
/* 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] = {
+static const NLType rtnl_address_types[] = {
[IFA_ADDRESS] = { .type = NETLINK_TYPE_IN_ADDR },
[IFA_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR },
[IFA_LABEL] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 },
@@ -464,7 +463,7 @@ static const NLTypeSystem rtnl_address_type_system = {
.types = rtnl_address_types,
};
-static const NLType rtnl_route_types[RTA_MAX + 1] = {
+static const NLType rtnl_route_types[] = {
[RTA_DST] = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */
[RTA_SRC] = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */
[RTA_IIF] = { .type = NETLINK_TYPE_U32 },
@@ -482,7 +481,11 @@ static const NLType rtnl_route_types[RTA_MAX + 1] = {
RTA_TABLE,
RTA_MARK,
RTA_MFC_STATS,
+ RTA_VIA,
+ RTA_NEWDST,
*/
+ [RTA_PREF] = { .type = NETLINK_TYPE_U8 },
+
};
static const NLTypeSystem rtnl_route_type_system = {
@@ -490,7 +493,7 @@ static const NLTypeSystem rtnl_route_type_system = {
.types = rtnl_route_types,
};
-static const NLType rtnl_neigh_types[NDA_MAX + 1] = {
+static const NLType rtnl_neigh_types[] = {
[NDA_DST] = { .type = NETLINK_TYPE_IN_ADDR },
[NDA_LLADDR] = { .type = NETLINK_TYPE_ETHER_ADDR },
[NDA_CACHEINFO] = { .type = NETLINK_TYPE_CACHE_INFO, .size = sizeof(struct nda_cacheinfo) },
@@ -506,7 +509,7 @@ static const NLTypeSystem rtnl_neigh_type_system = {
.types = rtnl_neigh_types,
};
-static const NLType rtnl_types[RTM_MAX + 1] = {
+static const NLType rtnl_types[] = {
[NLMSG_DONE] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = 0 },
[NLMSG_ERROR] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = sizeof(struct nlmsgerr) },
[RTM_NEWLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
diff --git a/src/libsystemd/sd-netlink/netlink-types.h b/src/libsystemd/sd-netlink/netlink-types.h
index bf7c641541..9e636a0b53 100644
--- a/src/libsystemd/sd-netlink/netlink-types.h
+++ b/src/libsystemd/sd-netlink/netlink-types.h
@@ -21,6 +21,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "macro.h"
+
enum {
NETLINK_TYPE_UNSPEC,
NETLINK_TYPE_U8, /* NLA_U8 */
diff --git a/src/libsystemd/sd-netlink/netlink-util.c b/src/libsystemd/sd-netlink/netlink-util.c
index 482ff6b1c2..b69969e426 100644
--- a/src/libsystemd/sd-netlink/netlink-util.c
+++ b/src/libsystemd/sd-netlink/netlink-util.c
@@ -19,14 +19,13 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-
#include "sd-netlink.h"
-#include "netlink-util.h"
#include "netlink-internal.h"
+#include "netlink-util.h"
int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name) {
- _cleanup_netlink_message_unref_ sd_netlink_message *message = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
int r;
assert(rtnl);
@@ -56,7 +55,7 @@ int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name) {
int rtnl_set_link_properties(sd_netlink **rtnl, int ifindex, const char *alias,
const struct ether_addr *mac, unsigned mtu) {
- _cleanup_netlink_message_unref_ sd_netlink_message *message = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
int r;
assert(rtnl);
diff --git a/src/libsystemd/sd-netlink/netlink-util.h b/src/libsystemd/sd-netlink/netlink-util.h
index 9df0aa28bf..a0e59e1ec6 100644
--- a/src/libsystemd/sd-netlink/netlink-util.h
+++ b/src/libsystemd/sd-netlink/netlink-util.h
@@ -21,9 +21,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "sd-netlink.h"
#include "util.h"
-#include "sd-netlink.h"
int rtnl_message_new_synthetic_error(int error, uint32_t serial, sd_netlink_message **ret);
uint32_t rtnl_message_get_serial(sd_netlink_message *m);
@@ -39,9 +39,3 @@ int rtnl_set_link_properties(sd_netlink **rtnl, int ifindex, const char *alias,
int rtnl_log_parse_error(int r);
int rtnl_log_create_error(int r);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_netlink*, sd_netlink_unref);
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_netlink_message*, sd_netlink_message_unref);
-
-#define _cleanup_netlink_unref_ _cleanup_(sd_netlink_unrefp)
-#define _cleanup_netlink_message_unref_ _cleanup_(sd_netlink_message_unrefp)
diff --git a/src/libsystemd/sd-netlink/rtnl-message.c b/src/libsystemd/sd-netlink/rtnl-message.c
index 2f31f4ee69..3e605db661 100644
--- a/src/libsystemd/sd-netlink/rtnl-message.c
+++ b/src/libsystemd/sd-netlink/rtnl-message.c
@@ -23,16 +23,16 @@
#include <stdbool.h>
#include <unistd.h>
-#include "util.h"
-#include "socket-util.h"
+#include "sd-netlink.h"
+
#include "formats-util.h"
-#include "refcnt.h"
#include "missing.h"
-
-#include "sd-netlink.h"
-#include "netlink-util.h"
#include "netlink-internal.h"
#include "netlink-types.h"
+#include "netlink-util.h"
+#include "refcnt.h"
+#include "socket-util.h"
+#include "util.h"
int sd_rtnl_message_route_set_dst_prefixlen(sd_netlink_message *m, unsigned char prefixlen) {
struct rtmsg *rtm;
@@ -84,6 +84,35 @@ int sd_rtnl_message_route_set_scope(sd_netlink_message *m, unsigned char scope)
return 0;
}
+int sd_rtnl_message_route_set_flags(sd_netlink_message *m, unsigned flags) {
+ struct rtmsg *rtm;
+
+ assert_return(m, -EINVAL);
+ assert_return(m->hdr, -EINVAL);
+ assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL);
+
+ rtm = NLMSG_DATA(m->hdr);
+
+ rtm->rtm_flags = flags;
+
+ return 0;
+}
+
+int sd_rtnl_message_route_get_flags(sd_netlink_message *m, unsigned *flags) {
+ struct rtmsg *rtm;
+
+ assert_return(m, -EINVAL);
+ assert_return(m->hdr, -EINVAL);
+ assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL);
+ assert_return(flags, -EINVAL);
+
+ rtm = NLMSG_DATA(m->hdr);
+
+ *flags = rtm->rtm_flags;
+
+ return 0;
+}
+
int sd_rtnl_message_route_get_family(sd_netlink_message *m, int *family) {
struct rtmsg *rtm;
@@ -99,6 +128,66 @@ int sd_rtnl_message_route_get_family(sd_netlink_message *m, int *family) {
return 0;
}
+int sd_rtnl_message_route_get_protocol(sd_netlink_message *m, unsigned char *protocol) {
+ struct rtmsg *rtm;
+
+ assert_return(m, -EINVAL);
+ assert_return(m->hdr, -EINVAL);
+ assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL);
+ assert_return(protocol, -EINVAL);
+
+ rtm = NLMSG_DATA(m->hdr);
+
+ *protocol = rtm->rtm_protocol;
+
+ return 0;
+}
+
+int sd_rtnl_message_route_get_scope(sd_netlink_message *m, unsigned char *scope) {
+ struct rtmsg *rtm;
+
+ assert_return(m, -EINVAL);
+ assert_return(m->hdr, -EINVAL);
+ assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL);
+ assert_return(scope, -EINVAL);
+
+ rtm = NLMSG_DATA(m->hdr);
+
+ *scope = rtm->rtm_scope;
+
+ return 0;
+}
+
+int sd_rtnl_message_route_get_tos(sd_netlink_message *m, unsigned char *tos) {
+ struct rtmsg *rtm;
+
+ assert_return(m, -EINVAL);
+ assert_return(m->hdr, -EINVAL);
+ assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL);
+ assert_return(tos, -EINVAL);
+
+ rtm = NLMSG_DATA(m->hdr);
+
+ *tos = rtm->rtm_tos;
+
+ return 0;
+}
+
+int sd_rtnl_message_route_get_table(sd_netlink_message *m, unsigned char *table) {
+ struct rtmsg *rtm;
+
+ assert_return(m, -EINVAL);
+ assert_return(m->hdr, -EINVAL);
+ assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL);
+ assert_return(table, -EINVAL);
+
+ rtm = NLMSG_DATA(m->hdr);
+
+ *table = rtm->rtm_table;
+
+ return 0;
+}
+
int sd_rtnl_message_route_get_dst_prefixlen(sd_netlink_message *m, unsigned char *dst_len) {
struct rtmsg *rtm;
diff --git a/src/libsystemd/sd-netlink/sd-netlink.c b/src/libsystemd/sd-netlink/sd-netlink.c
index d248869c8d..15d387df2c 100644
--- a/src/libsystemd/sd-netlink/sd-netlink.c
+++ b/src/libsystemd/sd-netlink/sd-netlink.c
@@ -19,20 +19,23 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/socket.h>
#include <poll.h>
-
-#include "missing.h"
-#include "macro.h"
-#include "util.h"
-#include "hashmap.h"
+#include <sys/socket.h>
#include "sd-netlink.h"
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "hashmap.h"
+#include "macro.h"
+#include "missing.h"
#include "netlink-internal.h"
#include "netlink-util.h"
+#include "socket-util.h"
+#include "util.h"
static int sd_netlink_new(sd_netlink **ret) {
- _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
assert_return(ret, -EINVAL);
@@ -68,7 +71,7 @@ static int sd_netlink_new(sd_netlink **ret) {
}
int sd_netlink_new_from_netlink(sd_netlink **ret, int fd) {
- _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
socklen_t addrlen;
int r;
@@ -102,7 +105,7 @@ static bool rtnl_pid_changed(sd_netlink *rtnl) {
}
int sd_netlink_open_fd(sd_netlink **ret, int fd) {
- _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
int r;
assert_return(ret, -EINVAL);
@@ -183,10 +186,11 @@ sd_netlink *sd_netlink_unref(sd_netlink *rtnl) {
sd_event_unref(rtnl->event);
while ((f = rtnl->match_callbacks)) {
- LIST_REMOVE(match_callbacks, rtnl->match_callbacks, f);
- free(f);
+ sd_netlink_remove_match(rtnl, f->type, f->callback, f->userdata);
}
+ hashmap_free(rtnl->broadcast_group_refs);
+
safe_close(rtnl->fd);
free(rtnl);
}
@@ -282,7 +286,7 @@ static int dispatch_rqueue(sd_netlink *rtnl, sd_netlink_message **message) {
}
static int process_timeout(sd_netlink *rtnl) {
- _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
struct reply_callback *c;
usec_t n;
int r;
@@ -372,7 +376,7 @@ static int process_match(sd_netlink *rtnl, sd_netlink_message *m) {
}
static int process_running(sd_netlink *rtnl, sd_netlink_message **ret) {
- _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
assert(rtnl);
@@ -414,7 +418,7 @@ null_message:
}
int sd_netlink_process(sd_netlink *rtnl, sd_netlink_message **ret) {
- RTNL_DONT_DESTROY(rtnl);
+ NETLINK_DONT_DESTROY(rtnl);
int r;
assert_return(rtnl, -EINVAL);
@@ -619,7 +623,7 @@ int sd_netlink_call(sd_netlink *rtnl,
received_serial = rtnl_message_get_serial(rtnl->rqueue[i]);
if (received_serial == serial) {
- _cleanup_netlink_message_unref_ sd_netlink_message *incoming = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *incoming = NULL;
uint16_t type;
incoming = rtnl->rqueue[i];
@@ -856,26 +860,33 @@ int sd_netlink_add_match(sd_netlink *rtnl,
switch (type) {
case RTM_NEWLINK:
- case RTM_SETLINK:
- case RTM_GETLINK:
case RTM_DELLINK:
- r = socket_join_broadcast_group(rtnl, RTNLGRP_LINK);
+ r = socket_broadcast_group_ref(rtnl, RTNLGRP_LINK);
if (r < 0)
return r;
break;
case RTM_NEWADDR:
- case RTM_GETADDR:
case RTM_DELADDR:
- r = socket_join_broadcast_group(rtnl, RTNLGRP_IPV4_IFADDR);
+ r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV4_IFADDR);
if (r < 0)
return r;
- r = socket_join_broadcast_group(rtnl, RTNLGRP_IPV6_IFADDR);
+ r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV6_IFADDR);
if (r < 0)
return r;
break;
+ case RTM_NEWROUTE:
+ case RTM_DELROUTE:
+ r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV4_ROUTE);
+ if (r < 0)
+ return r;
+
+ r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV6_ROUTE);
+ if (r < 0)
+ return r;
+ break;
default:
return -EOPNOTSUPP;
}
@@ -892,23 +903,50 @@ int sd_netlink_remove_match(sd_netlink *rtnl,
sd_netlink_message_handler_t callback,
void *userdata) {
struct match_callback *c;
+ int r;
assert_return(rtnl, -EINVAL);
assert_return(callback, -EINVAL);
assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
- /* we should unsubscribe from the broadcast groups at this point, but it is not so
- trivial for a few reasons: the refcounting is a bit of a mess and not obvious
- how it will look like after we add genetlink support, and it is also not possible
- to query what broadcast groups were subscribed to when we inherit the socket to get
- the initial refcount. The latter could indeed be done for the first 32 broadcast
- groups (which incidentally is all we currently support in .socket units anyway),
- but we better not rely on only ever using 32 groups. */
LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks)
if (c->callback == callback && c->type == type && c->userdata == userdata) {
LIST_REMOVE(match_callbacks, rtnl->match_callbacks, c);
free(c);
+ switch (type) {
+ case RTM_NEWLINK:
+ case RTM_DELLINK:
+ r = socket_broadcast_group_unref(rtnl, RTNLGRP_LINK);
+ if (r < 0)
+ return r;
+
+ break;
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ r = socket_broadcast_group_unref(rtnl, RTNLGRP_IPV4_IFADDR);
+ if (r < 0)
+ return r;
+
+ r = socket_broadcast_group_unref(rtnl, RTNLGRP_IPV6_IFADDR);
+ if (r < 0)
+ return r;
+
+ break;
+ case RTM_NEWROUTE:
+ case RTM_DELROUTE:
+ r = socket_broadcast_group_unref(rtnl, RTNLGRP_IPV4_ROUTE);
+ if (r < 0)
+ return r;
+
+ r = socket_broadcast_group_unref(rtnl, RTNLGRP_IPV6_ROUTE);
+ if (r < 0)
+ return r;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
return 1;
}
diff --git a/src/libsystemd/sd-netlink/test-local-addresses.c b/src/libsystemd/sd-netlink/test-local-addresses.c
index 9867eec065..0b53297ab8 100644
--- a/src/libsystemd/sd-netlink/test-local-addresses.c
+++ b/src/libsystemd/sd-netlink/test-local-addresses.c
@@ -19,9 +19,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "af-list.h"
+#include "alloc-util.h"
#include "in-addr-util.h"
#include "local-addresses.h"
-#include "af-list.h"
static void print_local_addresses(struct local_address *a, unsigned n) {
unsigned i;
diff --git a/src/libsystemd/sd-netlink/test-netlink.c b/src/libsystemd/sd-netlink/test-netlink.c
index c9cb415ca0..6a8b4d31d1 100644
--- a/src/libsystemd/sd-netlink/test-netlink.c
+++ b/src/libsystemd/sd-netlink/test-netlink.c
@@ -19,19 +19,21 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <netinet/ether.h>
#include <net/if.h>
+#include <netinet/ether.h>
-#include "util.h"
-#include "macro.h"
#include "sd-netlink.h"
-#include "socket-util.h"
-#include "netlink-util.h"
-#include "event-util.h"
+
+#include "ether-addr-util.h"
+#include "macro.h"
#include "missing.h"
+#include "netlink-util.h"
+#include "socket-util.h"
+#include "string-util.h"
+#include "util.h"
static void test_message_link_bridge(sd_netlink *rtnl) {
- _cleanup_netlink_message_unref_ sd_netlink_message *message = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
uint32_t cost;
assert_se(sd_rtnl_message_new_link(rtnl, &message, RTM_NEWLINK, 1) >= 0);
@@ -49,7 +51,7 @@ static void test_message_link_bridge(sd_netlink *rtnl) {
}
static void test_link_configure(sd_netlink *rtnl, int ifindex) {
- _cleanup_netlink_message_unref_ sd_netlink_message *message = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
const char *mac = "98:fe:94:3f:c6:18", *name = "test";
char buffer[ETHER_ADDR_TO_STRING_MAX];
unsigned int mtu = 1450, mtu_out;
@@ -143,7 +145,7 @@ static void test_address_get(sd_netlink *rtnl, int ifindex) {
}
static void test_route(void) {
- _cleanup_netlink_message_unref_ sd_netlink_message *req;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req;
struct in_addr addr, addr_data;
uint32_t index = 2, u32_data;
int r;
@@ -206,9 +208,9 @@ static int link_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata)
}
static void test_event_loop(int ifindex) {
- _cleanup_event_unref_ sd_event *event = NULL;
- _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
- _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
char *ifname;
ifname = strdup("lo2");
@@ -246,8 +248,8 @@ static int pipe_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata)
}
static void test_async(int ifindex) {
- _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
- _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL, *r = NULL;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL, *r = NULL;
uint32_t serial;
char *ifname;
@@ -267,8 +269,8 @@ static void test_async(int ifindex) {
}
static void test_pipe(int ifindex) {
- _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
- _cleanup_netlink_message_unref_ sd_netlink_message *m1 = NULL, *m2 = NULL;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m1 = NULL, *m2 = NULL;
int counter = 0;
assert_se(sd_netlink_open(&rtnl) >= 0);
@@ -291,7 +293,7 @@ static void test_pipe(int ifindex) {
}
static void test_container(void) {
- _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
uint16_t u16_data;
uint32_t u32_data;
const char *string_data;
@@ -326,7 +328,7 @@ static void test_container(void) {
}
static void test_match(void) {
- _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
assert_se(sd_netlink_open(&rtnl) >= 0);
@@ -341,7 +343,7 @@ static void test_match(void) {
}
static void test_get_addresses(sd_netlink *rtnl) {
- _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
sd_netlink_message *m;
assert_se(sd_rtnl_message_new_addr(rtnl, &req, RTM_GETADDR, 0, AF_UNSPEC) >= 0);
@@ -369,7 +371,7 @@ static void test_get_addresses(sd_netlink *rtnl) {
}
static void test_message(void) {
- _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
assert_se(rtnl_message_new_synthetic_error(-ETIMEDOUT, 1, &m) >= 0);
assert_se(sd_netlink_message_get_errno(m) == -ETIMEDOUT);
diff --git a/src/libsystemd/sd-network/network-util.c b/src/libsystemd/sd-network/network-util.c
index 48958e8a9f..a2d6c59314 100644
--- a/src/libsystemd/sd-network/network-util.c
+++ b/src/libsystemd/sd-network/network-util.c
@@ -19,8 +19,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "strv.h"
+#include "alloc-util.h"
+#include "fd-util.h"
#include "network-util.h"
+#include "strv.h"
bool network_is_online(void) {
_cleanup_free_ char *state = NULL;
diff --git a/src/libsystemd/sd-network/network-util.h b/src/libsystemd/sd-network/network-util.h
index 11a0012348..57bbb70f41 100644
--- a/src/libsystemd/sd-network/network-util.h
+++ b/src/libsystemd/sd-network/network-util.h
@@ -23,7 +23,4 @@
#include "sd-network.h"
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_network_monitor*, sd_network_monitor_unref);
-#define _cleanup_network_monitor_unref_ _cleanup_(sd_network_monitor_unrefp)
-
bool network_is_online(void);
diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c
index 87d87359b8..c1f5867ee4 100644
--- a/src/libsystemd/sd-network/sd-network.c
+++ b/src/libsystemd/sd-network/sd-network.c
@@ -20,16 +20,22 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <string.h>
#include <errno.h>
-#include <sys/inotify.h>
#include <poll.h>
+#include <string.h>
+#include <sys/inotify.h>
-#include "util.h"
+#include "sd-network.h"
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
#include "macro.h"
+#include "parse-util.h"
+#include "string-util.h"
#include "strv.h"
-#include "fileio.h"
-#include "sd-network.h"
+#include "util.h"
_public_ int sd_network_get_operational_state(char **state) {
_cleanup_free_ char *s = NULL;
@@ -93,17 +99,17 @@ _public_ int sd_network_get_domains(char ***ret) {
return network_get_strv("DOMAINS", ret);
}
-_public_ int sd_network_link_get_setup_state(int ifindex, char **state) {
+static int network_link_get_string(int ifindex, const char *field, char **ret) {
_cleanup_free_ char *s = NULL, *p = NULL;
int r;
assert_return(ifindex > 0, -EINVAL);
- assert_return(state, -EINVAL);
+ assert_return(ret, -EINVAL);
- if (asprintf(&p, "/run/systemd/netif/links/%d", ifindex) < 0)
+ if (asprintf(&p, "/run/systemd/netif/links/%i", ifindex) < 0)
return -ENOMEM;
- r = parse_env_file(p, NEWLINE, "ADMIN_STATE", &s, NULL);
+ r = parse_env_file(p, NEWLINE, field, &s, NULL);
if (r == -ENOENT)
return -ENODATA;
if (r < 0)
@@ -111,82 +117,72 @@ _public_ int sd_network_link_get_setup_state(int ifindex, char **state) {
if (isempty(s))
return -ENODATA;
- *state = s;
+ *ret = s;
s = NULL;
return 0;
}
-_public_ int sd_network_link_get_network_file(int ifindex, char **filename) {
- _cleanup_free_ char *s = NULL, *p = NULL;
+static int network_link_get_strv(int ifindex, const char *key, char ***ret) {
+ _cleanup_free_ char *p = NULL, *s = NULL;
+ _cleanup_strv_free_ char **a = NULL;
int r;
assert_return(ifindex > 0, -EINVAL);
- assert_return(filename, -EINVAL);
+ assert_return(ret, -EINVAL);
if (asprintf(&p, "/run/systemd/netif/links/%d", ifindex) < 0)
return -ENOMEM;
- r = parse_env_file(p, NEWLINE, "NETWORK_FILE", &s, NULL);
+ r = parse_env_file(p, NEWLINE, key, &s, NULL);
if (r == -ENOENT)
return -ENODATA;
if (r < 0)
return r;
- if (isempty(s))
- return -ENODATA;
-
- *filename = s;
- s = NULL;
+ if (isempty(s)) {
+ *ret = NULL;
+ return 0;
+ }
- return 0;
-}
+ a = strv_split(s, " ");
+ if (!a)
+ return -ENOMEM;
-_public_ int sd_network_link_get_operational_state(int ifindex, char **state) {
- _cleanup_free_ char *s = NULL, *p = NULL;
- int r;
+ strv_uniq(a);
+ r = strv_length(a);
- assert_return(ifindex > 0, -EINVAL);
- assert_return(state, -EINVAL);
+ *ret = a;
+ a = NULL;
- if (asprintf(&p, "/run/systemd/netif/links/%d", ifindex) < 0)
- return -ENOMEM;
+ return r;
+}
- r = parse_env_file(p, NEWLINE, "OPER_STATE", &s, NULL);
- if (r == -ENOENT)
- return -ENODATA;
- if (r < 0)
- return r;
- if (isempty(s))
- return -ENODATA;
+_public_ int sd_network_link_get_setup_state(int ifindex, char **state) {
+ return network_link_get_string(ifindex, "ADMIN_STATE", state);
+}
- *state = s;
- s = NULL;
+_public_ int sd_network_link_get_network_file(int ifindex, char **filename) {
+ return network_link_get_string(ifindex, "NETWORK_FILE", filename);
+}
- return 0;
+_public_ int sd_network_link_get_operational_state(int ifindex, char **state) {
+ return network_link_get_string(ifindex, "OPER_STATE", state);
}
_public_ int sd_network_link_get_llmnr(int ifindex, char **llmnr) {
- _cleanup_free_ char *s = NULL, *p = NULL;
- int r;
-
- assert_return(ifindex > 0, -EINVAL);
- assert_return(llmnr, -EINVAL);
-
- if (asprintf(&p, "/run/systemd/netif/links/%d", ifindex) < 0)
- return -ENOMEM;
+ return network_link_get_string(ifindex, "LLMNR", llmnr);
+}
- r = parse_env_file(p, NEWLINE, "LLMNR", &s, NULL);
- if (r == -ENOENT)
- return -ENODATA;
- if (r < 0)
- return r;
- if (isempty(s))
- return -ENODATA;
+_public_ int sd_network_link_get_mdns(int ifindex, char **mdns) {
+ return network_link_get_string(ifindex, "MDNS", mdns);
+}
- *llmnr = s;
- s = NULL;
+_public_ int sd_network_link_get_dnssec(int ifindex, char **dnssec) {
+ return network_link_get_string(ifindex, "DNSSEC", dnssec);
+}
- return 0;
+_public_ int sd_network_link_get_dnssec_negative_trust_anchors(int ifindex, char ***nta) {
+ return network_link_get_strv(ifindex, "DNSSEC_NTA", nta);
}
_public_ int sd_network_link_get_lldp(int ifindex, char **lldp) {
@@ -215,85 +211,32 @@ _public_ int sd_network_link_get_lldp(int ifindex, char **lldp) {
}
int sd_network_link_get_timezone(int ifindex, char **ret) {
- _cleanup_free_ char *s = NULL, *p = NULL;
- int r;
-
- assert_return(ifindex > 0, -EINVAL);
- assert_return(ret, -EINVAL);
-
- if (asprintf(&p, "/run/systemd/netif/links/%d", ifindex) < 0)
- return -ENOMEM;
-
- r = parse_env_file(p, NEWLINE, "TIMEZONE", &s, NULL);
- if (r == -ENOENT)
- return -ENODATA;
- if (r < 0)
- return r;
- if (isempty(s))
- return -ENODATA;
-
- *ret = s;
- s = NULL;
- return 0;
-}
-
-static int network_get_link_strv(const char *key, int ifindex, char ***ret) {
- _cleanup_free_ char *p = NULL, *s = NULL;
- _cleanup_strv_free_ char **a = NULL;
- int r;
-
- assert_return(ifindex > 0, -EINVAL);
- assert_return(ret, -EINVAL);
-
- if (asprintf(&p, "/run/systemd/netif/links/%d", ifindex) < 0)
- return -ENOMEM;
-
- r = parse_env_file(p, NEWLINE, key, &s, NULL);
- if (r == -ENOENT)
- return -ENODATA;
- if (r < 0)
- return r;
- if (isempty(s)) {
- *ret = NULL;
- return 0;
- }
-
- a = strv_split(s, " ");
- if (!a)
- return -ENOMEM;
-
- strv_uniq(a);
- r = strv_length(a);
-
- *ret = a;
- a = NULL;
-
- return r;
+ return network_link_get_string(ifindex, "TIMEZONE", ret);
}
_public_ int sd_network_link_get_dns(int ifindex, char ***ret) {
- return network_get_link_strv("DNS", ifindex, ret);
+ return network_link_get_strv(ifindex, "DNS", ret);
}
_public_ int sd_network_link_get_ntp(int ifindex, char ***ret) {
- return network_get_link_strv("NTP", ifindex, ret);
+ return network_link_get_strv(ifindex, "NTP", ret);
}
_public_ int sd_network_link_get_domains(int ifindex, char ***ret) {
- return network_get_link_strv("DOMAINS", ifindex, ret);
+ return network_link_get_strv(ifindex, "DOMAINS", ret);
}
_public_ int sd_network_link_get_carrier_bound_to(int ifindex, char ***ret) {
- return network_get_link_strv("CARRIER_BOUND_TO", ifindex, ret);
+ return network_link_get_strv(ifindex, "CARRIER_BOUND_TO", ret);
}
_public_ int sd_network_link_get_carrier_bound_by(int ifindex, char ***ret) {
- return network_get_link_strv("CARRIER_BOUND_BY", ifindex, ret);
+ return network_link_get_strv(ifindex, "CARRIER_BOUND_BY", ret);
}
_public_ int sd_network_link_get_wildcard_domain(int ifindex) {
- int r;
_cleanup_free_ char *p = NULL, *s = NULL;
+ int r;
assert_return(ifindex > 0, -EINVAL);
diff --git a/src/libsystemd/sd-path/sd-path.c b/src/libsystemd/sd-path/sd-path.c
index 7363be2794..480f1ad065 100644
--- a/src/libsystemd/sd-path/sd-path.c
+++ b/src/libsystemd/sd-path/sd-path.c
@@ -17,12 +17,18 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
+#include "sd-path.h"
+
+#include "alloc-util.h"
#include "architecture.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "missing.h"
#include "path-util.h"
+#include "string-util.h"
#include "strv.h"
-#include "sd-path.h"
-#include "missing.h"
+#include "user-util.h"
+#include "util.h"
static int from_environment(const char *envname, const char *fallback, const char **ret) {
assert(ret);
diff --git a/src/libsystemd/sd-resolve/sd-resolve.c b/src/libsystemd/sd-resolve/sd-resolve.c
index 888b372c99..d6e6f396d4 100644
--- a/src/libsystemd/sd-resolve/sd-resolve.c
+++ b/src/libsystemd/sd-resolve/sd-resolve.c
@@ -19,24 +19,27 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <signal.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
#include <errno.h>
+#include <poll.h>
+#include <pthread.h>
#include <resolv.h>
+#include <signal.h>
#include <stdint.h>
-#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <sys/prctl.h>
-#include <poll.h>
+#include <unistd.h>
-#include "util.h"
+#include "sd-resolve.h"
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "io-util.h"
#include "list.h"
-#include "socket-util.h"
#include "missing.h"
-#include "resolve-util.h"
-#include "sd-resolve.h"
+#include "socket-util.h"
+#include "util.h"
#define WORKERS_MIN 1U
#define WORKERS_MAX 16U
@@ -175,7 +178,7 @@ static int getnameinfo_done(sd_resolve_query *q);
static void resolve_query_disconnect(sd_resolve_query *q);
#define RESOLVE_DONT_DESTROY(resolve) \
- _cleanup_resolve_unref_ _unused_ sd_resolve *_dont_destroy_##resolve = sd_resolve_ref(resolve)
+ _cleanup_(sd_resolve_unrefp) _unused_ sd_resolve *_dont_destroy_##resolve = sd_resolve_ref(resolve)
static int send_died(int out_fd) {
@@ -580,12 +583,8 @@ static void resolve_free(sd_resolve *resolve) {
}
/* Now terminate them and wait until they are gone. */
- for (i = 0; i < resolve->n_valid_workers; i++) {
- for (;;) {
- if (pthread_join(resolve->workers[i], NULL) != EINTR)
- break;
- }
- }
+ for (i = 0; i < resolve->n_valid_workers; i++)
+ pthread_join(resolve->workers[i], NULL);
/* Close all communication channels */
for (i = 0; i < _FD_MAX; i++)
diff --git a/src/libsystemd/sd-resolve/test-resolve.c b/src/libsystemd/sd-resolve/test-resolve.c
index e8056529f5..ce97e81ed6 100644
--- a/src/libsystemd/sd-resolve/test-resolve.c
+++ b/src/libsystemd/sd-resolve/test-resolve.c
@@ -20,18 +20,20 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <string.h>
-#include <sys/socket.h>
#include <arpa/inet.h>
-#include <stdio.h>
+#include <errno.h>
#include <netinet/in.h>
#include <resolv.h>
-#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
-#include "socket-util.h"
#include "sd-resolve.h"
-#include "resolve-util.h"
+
+#include "alloc-util.h"
#include "macro.h"
+#include "socket-util.h"
+#include "string-util.h"
static int getaddrinfo_handler(sd_resolve_query *q, int ret, const struct addrinfo *ai, void *userdata) {
const struct addrinfo *i;
@@ -68,8 +70,8 @@ static int getnameinfo_handler(sd_resolve_query *q, int ret, const char *host, c
}
int main(int argc, char *argv[]) {
- _cleanup_resolve_query_unref_ sd_resolve_query *q1 = NULL, *q2 = NULL;
- _cleanup_resolve_unref_ sd_resolve *resolve = NULL;
+ _cleanup_(sd_resolve_query_unrefp) sd_resolve_query *q1 = NULL, *q2 = NULL;
+ _cleanup_(sd_resolve_unrefp) sd_resolve *resolve = NULL;
int r = 0;
struct addrinfo hints = {
@@ -99,11 +101,11 @@ int main(int argc, char *argv[]) {
if (r < 0)
log_error_errno(r, "sd_resolve_getnameinfo(): %m");
- /* Wait until the two queries are completed */
- while (sd_resolve_query_is_done(q1) == 0 ||
- sd_resolve_query_is_done(q2) == 0) {
-
+ /* Wait until all queries are completed */
+ for (;;) {
r = sd_resolve_wait(resolve, (uint64_t) -1);
+ if (r == 0)
+ break;
if (r < 0) {
log_error_errno(r, "sd_resolve_wait(): %m");
assert_not_reached("sd_resolve_wait() failed");
diff --git a/src/libsystemd/sd-utf8/sd-utf8.c b/src/libsystemd/sd-utf8/sd-utf8.c
index 6f2aa6064c..9e52db3b3d 100644
--- a/src/libsystemd/sd-utf8/sd-utf8.c
+++ b/src/libsystemd/sd-utf8/sd-utf8.c
@@ -19,10 +19,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
-#include "utf8.h"
#include "sd-utf8.h"
+#include "utf8.h"
+#include "util.h"
+
_public_ const char *sd_utf8_is_valid(const char *s) {
assert_return(s, NULL);
diff --git a/src/libudev/libudev-device-internal.h b/src/libudev/libudev-device-internal.h
index aa36b8cb12..40d59201cf 100644
--- a/src/libudev/libudev-device-internal.h
+++ b/src/libudev/libudev-device-internal.h
@@ -21,9 +21,10 @@
#pragma once
#include "libudev.h"
-#include "libudev-private.h"
#include "sd-device.h"
+#include "libudev-private.h"
+
/**
* udev_device:
*
diff --git a/src/libudev/libudev-device-private.c b/src/libudev/libudev-device-private.c
index 4b9c053b54..2aae0726c1 100644
--- a/src/libudev/libudev-device-private.c
+++ b/src/libudev/libudev-device-private.c
@@ -19,10 +19,10 @@
***/
#include "libudev.h"
-#include "libudev-private.h"
-#include "libudev-device-internal.h"
#include "device-private.h"
+#include "libudev-device-internal.h"
+#include "libudev-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;
@@ -137,14 +137,10 @@ gid_t udev_device_get_devnode_gid(struct udev_device *udev_device) {
}
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);
+ device_ensure_usec_initialized(udev_device->device,
+ udev_device_old ? udev_device_old->device : NULL);
}
char **udev_device_get_properties_envp(struct udev_device *udev_device) {
diff --git a/src/libudev/libudev-device.c b/src/libudev/libudev-device.c
index 9a8d682107..814e016800 100644
--- a/src/libudev/libudev-device.c
+++ b/src/libudev/libudev-device.c
@@ -18,29 +18,31 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
-#include <stdlib.h>
-#include <stddef.h>
-#include <unistd.h>
-#include <stdbool.h>
-#include <errno.h>
-#include <string.h>
+#include <ctype.h>
#include <dirent.h>
+#include <errno.h>
#include <fcntl.h>
-#include <ctype.h>
+#include <linux/sockios.h>
#include <net/if.h>
-#include <sys/stat.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
-#include <linux/sockios.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "libudev.h"
#include "sd-device.h"
-#include "device-util.h"
-#include "device-private.h"
-#include "libudev.h"
-#include "libudev-private.h"
+#include "alloc-util.h"
+#include "device-private.h"
+#include "device-util.h"
#include "libudev-device-internal.h"
+#include "libudev-private.h"
+#include "parse-util.h"
/**
* SECTION:libudev-device
diff --git a/src/libudev/libudev-enumerate.c b/src/libudev/libudev-enumerate.c
index df088946df..e416e178b4 100644
--- a/src/libudev/libudev-enumerate.c
+++ b/src/libudev/libudev-enumerate.c
@@ -18,22 +18,23 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
-#include <stdlib.h>
-#include <stddef.h>
-#include <errno.h>
-#include <string.h>
#include <dirent.h>
+#include <errno.h>
#include <fnmatch.h>
#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <sys/stat.h>
#include "libudev.h"
-#include "libudev-device-internal.h"
#include "sd-device.h"
-#include "device-util.h"
-#include "device-enumerator-private.h"
+#include "alloc-util.h"
+#include "device-enumerator-private.h"
+#include "device-util.h"
+#include "libudev-device-internal.h"
/**
* SECTION:libudev-enumerate
@@ -369,7 +370,7 @@ _public_ int udev_enumerate_add_match_sysname(struct udev_enumerate *udev_enumer
* Returns: 0 on success, otherwise a negative error value.
*/
_public_ int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, const char *syspath) {
- _cleanup_device_unref_ sd_device *device = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
int r;
assert_return(udev_enumerate, -EINVAL);
diff --git a/src/libudev/libudev-hwdb.c b/src/libudev/libudev-hwdb.c
index 98951fb85b..a53f000015 100644
--- a/src/libudev/libudev-hwdb.c
+++ b/src/libudev/libudev-hwdb.c
@@ -17,9 +17,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "libudev-private.h"
#include "sd-hwdb.h"
+
+#include "alloc-util.h"
#include "hwdb-util.h"
+#include "libudev-private.h"
/**
* SECTION:libudev-hwdb
@@ -51,7 +53,7 @@ struct udev_hwdb {
* Returns: a hwdb context.
**/
_public_ struct udev_hwdb *udev_hwdb_new(struct udev *udev) {
- _cleanup_hwdb_unref_ sd_hwdb *hwdb_internal = NULL;
+ _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb_internal = NULL;
struct udev_hwdb *hwdb;
int r;
diff --git a/src/libudev/libudev-list.c b/src/libudev/libudev-list.c
index 19e9130be0..da496ed456 100644
--- a/src/libudev/libudev-list.c
+++ b/src/libudev/libudev-list.c
@@ -17,11 +17,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
-#include <stddef.h>
#include <errno.h>
+#include <stddef.h>
+#include <stdlib.h>
#include <string.h>
+#include "alloc-util.h"
#include "libudev-private.h"
/**
diff --git a/src/libudev/libudev-monitor.c b/src/libudev/libudev-monitor.c
index 282aa2b0d9..f870eba9eb 100644
--- a/src/libudev/libudev-monitor.c
+++ b/src/libudev/libudev-monitor.c
@@ -17,22 +17,28 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
+#include <linux/filter.h>
+#include <linux/netlink.h>
+#include <poll.h>
+#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
-#include <stddef.h>
-#include <unistd.h>
-#include <errno.h>
#include <string.h>
-#include <poll.h>
#include <sys/socket.h>
-#include <linux/netlink.h>
-#include <linux/filter.h>
+#include <unistd.h>
#include "libudev.h"
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "formats-util.h"
#include "libudev-private.h"
-#include "socket-util.h"
#include "missing.h"
-#include "formats-util.h"
+#include "mount-util.h"
+#include "socket-util.h"
+#include "string-util.h"
/**
* SECTION:libudev-monitor
@@ -408,10 +414,8 @@ _public_ int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor)
if (err >= 0)
monitor_set_nl_address(udev_monitor);
- else {
- log_debug_errno(errno, "bind failed: %m");
- return -errno;
- }
+ else
+ return log_debug_errno(errno, "bind failed: %m");
/* enable receiving of sender credentials */
err = setsockopt(udev_monitor->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
diff --git a/src/libudev/libudev-private.h b/src/libudev/libudev-private.h
index 1240ea79cc..52c5075110 100644
--- a/src/libudev/libudev-private.h
+++ b/src/libudev/libudev-private.h
@@ -21,14 +21,15 @@
#define _LIBUDEV_PRIVATE_H_
#include <signal.h>
-#include <stdint.h>
#include <stdbool.h>
+#include <stdint.h>
#include "libudev.h"
+
#include "macro.h"
-#include "util.h"
#include "mkdir.h"
#include "strxcpyx.h"
+#include "util.h"
#define READ_END 0
#define WRITE_END 1
@@ -135,8 +136,6 @@ int udev_queue_export_device_finished(struct udev_queue_export *udev_queue_expor
#define UTIL_NAME_SIZE 512
#define UTIL_LINE_SIZE 16384
#define UDEV_ALLOWED_CHARS_INPUT "/ $%?,"
-ssize_t util_get_sys_core_link_value(struct udev *udev, const char *slink, const char *syspath, char *value, size_t size);
-int util_resolve_sys_link(struct udev *udev, char *syspath, size_t size);
int util_log_priority(const char *priority);
size_t util_path_encode(const char *src, char *dest, size_t size);
void util_remove_trailing_chars(char *path, char c);
diff --git a/src/libudev/libudev-queue.c b/src/libudev/libudev-queue.c
index 11e15d13e6..e3dffa6925 100644
--- a/src/libudev/libudev-queue.c
+++ b/src/libudev/libudev-queue.c
@@ -18,12 +18,15 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
-#include <stddef.h>
-#include <unistd.h>
#include <errno.h>
+#include <stddef.h>
+#include <stdlib.h>
#include <sys/inotify.h>
+#include <unistd.h>
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "io-util.h"
#include "libudev-private.h"
/**
diff --git a/src/libudev/libudev-util.c b/src/libudev/libudev-util.c
index f4656277c6..574cfeac85 100644
--- a/src/libudev/libudev-util.c
+++ b/src/libudev/libudev-util.c
@@ -17,18 +17,20 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
-#include <stddef.h>
-#include <unistd.h>
+#include <ctype.h>
#include <errno.h>
+#include <stddef.h>
+#include <stdlib.h>
#include <string.h>
-#include <ctype.h>
+#include <unistd.h>
-#include "device-nodes.h"
#include "libudev.h"
+
+#include "MurmurHash2.h"
+#include "device-nodes.h"
#include "libudev-private.h"
+#include "syslog-util.h"
#include "utf8.h"
-#include "MurmurHash2.h"
/**
* SECTION:libudev-util
@@ -100,52 +102,6 @@ int util_resolve_subsys_kernel(struct udev *udev, const char *string,
return 0;
}
-ssize_t util_get_sys_core_link_value(struct udev *udev, const char *slink, const char *syspath, char *value, size_t size)
-{
- char path[UTIL_PATH_SIZE];
- char target[UTIL_PATH_SIZE];
- ssize_t len;
- const char *pos;
-
- strscpyl(path, sizeof(path), syspath, "/", slink, NULL);
- len = readlink(path, target, sizeof(target));
- if (len <= 0 || len == (ssize_t)sizeof(target))
- return -1;
- target[len] = '\0';
- pos = strrchr(target, '/');
- if (pos == NULL)
- return -1;
- pos = &pos[1];
- return strscpy(value, size, pos);
-}
-
-int util_resolve_sys_link(struct udev *udev, char *syspath, size_t size)
-{
- char link_target[UTIL_PATH_SIZE];
-
- ssize_t len;
- int i;
- int back;
- char *base = NULL;
-
- len = readlink(syspath, link_target, sizeof(link_target));
- if (len <= 0 || len == (ssize_t)sizeof(link_target))
- return -1;
- link_target[len] = '\0';
-
- for (back = 0; startswith(&link_target[back * 3], "../"); back++)
- ;
- for (i = 0; i <= back; i++) {
- base = strrchr(syspath, '/');
- if (base == NULL)
- return -EINVAL;
- base[0] = '\0';
- }
-
- strscpyl(base, size - (base - syspath), "/", &link_target[back * 3], NULL);
- return 0;
-}
-
int util_log_priority(const char *priority)
{
char *endptr;
diff --git a/src/libudev/libudev.c b/src/libudev/libudev.c
index ec15d2576b..63fb05547d 100644
--- a/src/libudev/libudev.c
+++ b/src/libudev/libudev.c
@@ -17,16 +17,20 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <ctype.h>
+#include <stdarg.h>
+#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
-#include <stddef.h>
-#include <stdarg.h>
#include <string.h>
-#include <ctype.h>
#include "libudev.h"
+
+#include "alloc-util.h"
+#include "fd-util.h"
#include "libudev-private.h"
#include "missing.h"
+#include "string-util.h"
/**
* SECTION:libudev
diff --git a/src/locale/localectl.c b/src/locale/localectl.c
index 4a91c7420a..db75bf4615 100644
--- a/src/locale/localectl.c
+++ b/src/locale/localectl.c
@@ -20,26 +20,27 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <ftw.h>
+#include <getopt.h>
#include <locale.h>
-#include <stdlib.h>
#include <stdbool.h>
-#include <getopt.h>
+#include <stdlib.h>
#include <string.h>
-#include <ftw.h>
#include "sd-bus.h"
-#include "bus-util.h"
+
#include "bus-error.h"
-#include "util.h"
-#include "spawn-polkit-agent.h"
-#include "build.h"
-#include "strv.h"
-#include "pager.h"
-#include "set.h"
+#include "bus-util.h"
#include "def.h"
-#include "virt.h"
+#include "fd-util.h"
#include "fileio.h"
#include "locale-util.h"
+#include "pager.h"
+#include "set.h"
+#include "spawn-polkit-agent.h"
+#include "strv.h"
+#include "util.h"
+#include "virt.h"
static bool arg_no_pager = false;
static bool arg_ask_password = true;
@@ -194,8 +195,8 @@ static int show_status(sd_bus *bus, char **args, unsigned n) {
}
static int set_locale(sd_bus *bus, char **args, unsigned n) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert(bus);
@@ -247,7 +248,7 @@ static int list_locales(sd_bus *bus, char **args, unsigned n) {
}
static int set_vconsole_keymap(sd_bus *bus, char **args, unsigned n) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
const char *map, *toggle_map;
int r;
@@ -350,7 +351,7 @@ static int list_vconsole_keymaps(sd_bus *bus, char **args, unsigned n) {
}
static int set_x11_keymap(sd_bus *bus, char **args, unsigned n) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
const char *layout, *model, *variant, *options;
int r;
@@ -546,9 +547,7 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case ARG_NO_CONVERT:
arg_convert = false;
@@ -667,7 +666,7 @@ static int localectl_main(sd_bus *bus, int argc, char *argv[]) {
}
int main(int argc, char*argv[]) {
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
setlocale(LC_ALL, "");
@@ -678,7 +677,7 @@ int main(int argc, char*argv[]) {
if (r <= 0)
goto finish;
- r = bus_open_transport(arg_transport, arg_host, false, &bus);
+ r = bus_connect_transport(arg_transport, arg_host, false, &bus);
if (r < 0) {
log_error_errno(r, "Failed to create bus connection: %m");
goto finish;
diff --git a/src/locale/localed.c b/src/locale/localed.c
index e3eef4a610..8ab845eb80 100644
--- a/src/locale/localed.c
+++ b/src/locale/localed.c
@@ -24,25 +24,28 @@
#include <string.h>
#include <unistd.h>
+#ifdef HAVE_XKBCOMMON
+#include <xkbcommon/xkbcommon.h>
+#endif
+
#include "sd-bus.h"
-#include "util.h"
-#include "mkdir.h"
-#include "strv.h"
+#include "alloc-util.h"
+#include "bus-error.h"
+#include "bus-message.h"
+#include "bus-util.h"
#include "def.h"
#include "env-util.h"
-#include "fileio.h"
+#include "fd-util.h"
#include "fileio-label.h"
-#include "bus-util.h"
-#include "bus-error.h"
-#include "bus-message.h"
-#include "event-util.h"
+#include "fileio.h"
#include "locale-util.h"
+#include "mkdir.h"
+#include "path-util.h"
#include "selinux-util.h"
-
-#ifdef HAVE_XKBCOMMON
-#include <xkbcommon/xkbcommon.h>
-#endif
+#include "strv.h"
+#include "user-util.h"
+#include "util.h"
enum {
/* We don't list LC_ALL here on purpose. People should be
@@ -323,7 +326,7 @@ static int locale_write_data(Context *c, char ***settings) {
static int locale_update_system_manager(Context *c, sd_bus *bus) {
_cleanup_free_ char **l_unset = NULL;
_cleanup_strv_free_ char **l_set = NULL;
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
sd_bus_error error = SD_BUS_ERROR_NULL;
unsigned c_set, c_unset, p;
int r;
@@ -496,7 +499,7 @@ fail:
}
static int vconsole_reload(sd_bus *bus) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert(bus);
@@ -536,7 +539,7 @@ static int read_next_mapping(const char* filename,
if (!fgets(line, sizeof(line), f)) {
if (ferror(f))
- return errno ? -errno : -EIO;
+ return errno > 0 ? -errno : -EIO;
return 0;
}
@@ -1255,7 +1258,7 @@ static const sd_bus_vtable locale_vtable[] = {
};
static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
assert(c);
@@ -1286,8 +1289,8 @@ static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
int main(int argc, char *argv[]) {
_cleanup_(context_free) Context context = {};
- _cleanup_event_unref_ sd_event *event = NULL;
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
log_set_target(LOG_TARGET_AUTO);
diff --git a/src/login/inhibit.c b/src/login/inhibit.c
index c53ea8add7..cb4a2d9e78 100644
--- a/src/login/inhibit.c
+++ b/src/login/inhibit.c
@@ -19,21 +19,24 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <fcntl.h>
#include <getopt.h>
-#include <stdlib.h>
#include <stdio.h>
+#include <stdlib.h>
#include <unistd.h>
-#include <fcntl.h>
#include "sd-bus.h"
-#include "bus-util.h"
+
+#include "alloc-util.h"
#include "bus-error.h"
-#include "util.h"
-#include "build.h"
-#include "strv.h"
+#include "bus-util.h"
+#include "fd-util.h"
#include "formats-util.h"
#include "process-util.h"
#include "signal-util.h"
+#include "strv.h"
+#include "user-util.h"
+#include "util.h"
static const char* arg_what = "idle:sleep:shutdown";
static const char* arg_who = NULL;
@@ -46,7 +49,7 @@ static enum {
} arg_action = ACTION_INHIBIT;
static int inhibit(sd_bus *bus, sd_bus_error *error) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
int r;
int fd;
@@ -74,7 +77,7 @@ static int inhibit(sd_bus *bus, sd_bus_error *error) {
}
static int print_inhibitors(sd_bus *bus, sd_bus_error *error) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
const char *what, *who, *why, *mode;
unsigned int uid, pid;
unsigned n = 0;
@@ -179,9 +182,7 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case ARG_WHAT:
arg_what = optarg;
@@ -222,8 +223,8 @@ static int parse_argv(int argc, char *argv[]) {
}
int main(int argc, char *argv[]) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
log_parse_environment();
diff --git a/src/login/loginctl.c b/src/login/loginctl.c
index be52518161..931b96fe51 100644
--- a/src/login/loginctl.c
+++ b/src/login/loginctl.c
@@ -19,31 +19,34 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
#include <errno.h>
-#include <string.h>
#include <getopt.h>
#include <locale.h>
+#include <string.h>
+#include <unistd.h>
#include "sd-bus.h"
-#include "bus-util.h"
+
+#include "alloc-util.h"
#include "bus-error.h"
+#include "bus-util.h"
+#include "cgroup-show.h"
+#include "cgroup-util.h"
#include "log.h"
-#include "util.h"
+#include "logs-show.h"
#include "macro.h"
#include "pager.h"
-#include "build.h"
+#include "parse-util.h"
+#include "process-util.h"
+#include "signal-util.h"
+#include "spawn-polkit-agent.h"
#include "strv.h"
-#include "unit-name.h"
#include "sysfs-show.h"
-#include "logs-show.h"
-#include "cgroup-show.h"
-#include "cgroup-util.h"
-#include "spawn-polkit-agent.h"
-#include "verbs.h"
-#include "process-util.h"
#include "terminal-util.h"
-#include "signal-util.h"
+#include "unit-name.h"
+#include "user-util.h"
+#include "util.h"
+#include "verbs.h"
static char **arg_property = NULL;
static bool arg_all = false;
@@ -85,12 +88,12 @@ static OutputFlags get_output_flags(void) {
arg_all * OUTPUT_SHOW_ALL |
arg_full * OUTPUT_FULL_WIDTH |
(!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
- on_tty() * OUTPUT_COLOR;
+ colors_enabled() * OUTPUT_COLOR;
}
static int list_sessions(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;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
const char *id, *user, *seat, *object;
sd_bus *bus = userdata;
unsigned k = 0;
@@ -136,8 +139,8 @@ static int list_sessions(int argc, char *argv[], void *userdata) {
}
static int list_users(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;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
const char *user, *object;
sd_bus *bus = userdata;
unsigned k = 0;
@@ -183,8 +186,8 @@ static int list_users(int argc, char *argv[], void *userdata) {
}
static int list_seats(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;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
const char *seat, *object;
sd_bus *bus = userdata;
unsigned k = 0;
@@ -229,8 +232,8 @@ static int list_seats(int argc, char *argv[], void *userdata) {
}
static int show_unit_cgroup(sd_bus *bus, const char *interface, const char *unit, pid_t leader) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_free_ char *path = NULL;
const char *cgroup;
int r;
@@ -781,8 +784,8 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte
}
static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert(bus);
@@ -870,8 +873,8 @@ static int show_session(int argc, char *argv[], void *userdata) {
}
for (i = 1; i < argc; i++) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message * reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message * reply = NULL;
const char *path = NULL;
r = sd_bus_call_method(
@@ -925,8 +928,8 @@ static int show_user(int argc, char *argv[], void *userdata) {
}
for (i = 1; i < argc; i++) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message * reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message * reply = NULL;
const char *path = NULL;
uid_t uid;
@@ -985,8 +988,8 @@ static int show_seat(int argc, char *argv[], void *userdata) {
}
for (i = 1; i < argc; i++) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message * reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message * reply = NULL;
const char *path = NULL;
r = sd_bus_call_method(
@@ -1019,7 +1022,7 @@ static int show_seat(int argc, char *argv[], void *userdata) {
}
static int activate(int argc, char *argv[], void *userdata) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
char *short_argv[3];
int r, i;
@@ -1065,7 +1068,7 @@ static int activate(int argc, char *argv[], void *userdata) {
}
static int kill_session(int argc, char *argv[], void *userdata) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
int r, i;
@@ -1097,7 +1100,7 @@ static int kill_session(int argc, char *argv[], void *userdata) {
}
static int enable_linger(int argc, char *argv[], void *userdata) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
char* short_argv[3];
bool b;
@@ -1147,7 +1150,7 @@ static int enable_linger(int argc, char *argv[], void *userdata) {
}
static int terminate_user(int argc, char *argv[], void *userdata) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
int r, i;
@@ -1181,7 +1184,7 @@ static int terminate_user(int argc, char *argv[], void *userdata) {
}
static int kill_user(int argc, char *argv[], void *userdata) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
int r, i;
@@ -1218,7 +1221,7 @@ static int kill_user(int argc, char *argv[], void *userdata) {
}
static int attach(int argc, char *argv[], void *userdata) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
int r, i;
@@ -1248,7 +1251,7 @@ static int attach(int argc, char *argv[], void *userdata) {
}
static int flush_devices(int argc, char *argv[], void *userdata) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
int r;
@@ -1272,7 +1275,7 @@ static int flush_devices(int argc, char *argv[], void *userdata) {
}
static int lock_sessions(int argc, char *argv[], void *userdata) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
int r;
@@ -1296,7 +1299,7 @@ static int lock_sessions(int argc, char *argv[], void *userdata) {
}
static int terminate_seat(int argc, char *argv[], void *userdata) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
int r, i;
@@ -1416,9 +1419,7 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case 'p': {
r = strv_extend(&arg_property, optarg);
@@ -1533,7 +1534,7 @@ static int loginctl_main(int argc, char *argv[], sd_bus *bus) {
}
int main(int argc, char *argv[]) {
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
setlocale(LC_ALL, "");
@@ -1544,7 +1545,7 @@ int main(int argc, char *argv[]) {
if (r <= 0)
goto finish;
- r = bus_open_transport(arg_transport, arg_host, false, &bus);
+ r = bus_connect_transport(arg_transport, arg_host, false, &bus);
if (r < 0) {
log_error_errno(r, "Failed to create bus connection: %m");
goto finish;
diff --git a/src/login/logind-acl.c b/src/login/logind-acl.c
index 466225d69c..d0dd569a03 100644
--- a/src/login/logind-acl.c
+++ b/src/login/logind-acl.c
@@ -22,12 +22,17 @@
#include <errno.h>
#include <string.h>
-#include "util.h"
-#include "formats-util.h"
#include "acl-util.h"
-#include "set.h"
+#include "alloc-util.h"
+#include "dirent-util.h"
+#include "escape.h"
+#include "fd-util.h"
+#include "formats-util.h"
#include "logind-acl.h"
+#include "set.h"
+#include "string-util.h"
#include "udev-util.h"
+#include "util.h"
static int flush_acl(acl_t acl) {
acl_entry_t i;
diff --git a/src/login/logind-acl.h b/src/login/logind-acl.h
index ec09843a78..1f55759798 100644
--- a/src/login/logind-acl.h
+++ b/src/login/logind-acl.h
@@ -21,9 +21,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
#include <stdbool.h>
-#include <libudev.h>
+#include <sys/types.h>
+
+#include "libudev.h"
#ifdef HAVE_ACL
diff --git a/src/login/logind-action.c b/src/login/logind-action.c
index f635fb1b63..4470522c55 100644
--- a/src/login/logind-action.c
+++ b/src/login/logind-action.c
@@ -21,15 +21,18 @@
#include <unistd.h>
-#include "conf-parser.h"
-#include "special.h"
-#include "sleep-config.h"
-#include "bus-util.h"
+#include "alloc-util.h"
#include "bus-error.h"
-#include "logind-action.h"
+#include "bus-util.h"
+#include "conf-parser.h"
#include "formats-util.h"
+#include "logind-action.h"
#include "process-util.h"
+#include "sleep-config.h"
+#include "special.h"
+#include "string-table.h"
#include "terminal-util.h"
+#include "user-util.h"
int manager_handle_action(
Manager *m,
@@ -58,7 +61,7 @@ int manager_handle_action(
[HANDLE_HYBRID_SLEEP] = SPECIAL_HYBRID_SLEEP_TARGET
};
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
InhibitWhat inhibit_operation;
Inhibitor *offending = NULL;
bool supported;
@@ -147,7 +150,6 @@ int manager_handle_action(
offending->uid, strna(u),
offending->pid, strna(comm));
- warn_melody();
return -EPERM;
}
diff --git a/src/login/logind-action.h b/src/login/logind-action.h
index ff98065371..63c279cde7 100644
--- a/src/login/logind-action.h
+++ b/src/login/logind-action.h
@@ -35,6 +35,7 @@ typedef enum HandleAction {
_HANDLE_ACTION_INVALID = -1
} HandleAction;
+#include "logind-inhibit.h"
#include "logind.h"
int manager_handle_action(
diff --git a/src/login/logind-button.c b/src/login/logind-button.c
index f40e35a8cb..b08b69dbfc 100644
--- a/src/login/logind-button.c
+++ b/src/login/logind-button.c
@@ -19,16 +19,20 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <string.h>
#include <errno.h>
#include <fcntl.h>
+#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <linux/input.h>
#include "sd-messages.h"
-#include "util.h"
+
+#include "alloc-util.h"
+#include "fd-util.h"
#include "logind-button.h"
+#include "string-util.h"
+#include "util.h"
Button* button_new(Manager *m, const char *name) {
Button *b;
diff --git a/src/login/logind-core.c b/src/login/logind-core.c
index 6c05c11dbd..2e14aa2d95 100644
--- a/src/login/logind-core.c
+++ b/src/login/logind-core.c
@@ -19,19 +19,22 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
-#include <sys/ioctl.h>
#include <fcntl.h>
#include <pwd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
#include <linux/vt.h>
-#include "strv.h"
-#include "cgroup-util.h"
-#include "bus-util.h"
+#include "alloc-util.h"
#include "bus-error.h"
-#include "udev-util.h"
+#include "bus-util.h"
+#include "cgroup-util.h"
+#include "fd-util.h"
#include "logind.h"
+#include "strv.h"
#include "terminal-util.h"
+#include "udev-util.h"
+#include "user-util.h"
int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_device) {
Device *d;
@@ -95,15 +98,16 @@ int manager_add_session(Manager *m, const char *id, Session **_session) {
int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user) {
User *u;
+ int r;
assert(m);
assert(name);
u = hashmap_get(m->users, UID_TO_PTR(uid));
if (!u) {
- u = user_new(m, uid, gid, name);
- if (!u)
- return -ENOMEM;
+ r = user_new(&u, m, uid, gid, name);
+ if (r < 0)
+ return r;
}
if (_user)
@@ -135,7 +139,7 @@ int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user) {
errno = 0;
p = getpwuid(uid);
if (!p)
- return errno ? -errno : -ENOENT;
+ return errno > 0 ? -errno : -ENOENT;
return manager_add_user(m, uid, p->pw_gid, p->pw_name, _user);
}
@@ -400,7 +404,7 @@ static int vt_is_busy(unsigned int vtnr) {
}
int manager_spawn_autovt(Manager *m, unsigned int vtnr) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
char name[sizeof("autovt@tty.service") + DECIMAL_STR_MAX(unsigned int)];
int r;
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index 22e37a1638..9eda4638e5 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -20,33 +20,40 @@
***/
#include <errno.h>
+#include <pwd.h>
#include <string.h>
#include <unistd.h>
-#include <pwd.h>
#include "sd-messages.h"
-#include "strv.h"
-#include "mkdir.h"
-#include "path-util.h"
-#include "special.h"
-#include "sleep-config.h"
-#include "fileio-label.h"
-#include "unit-name.h"
-#include "audit.h"
-#include "bus-util.h"
-#include "bus-error.h"
+
+#include "alloc-util.h"
+#include "audit-util.h"
#include "bus-common-errors.h"
-#include "udev-util.h"
-#include "selinux-util.h"
+#include "bus-error.h"
+#include "bus-util.h"
+#include "dirent-util.h"
#include "efivars.h"
-#include "logind.h"
+#include "escape.h"
+#include "fd-util.h"
+#include "fileio-label.h"
#include "formats-util.h"
+#include "fs-util.h"
+#include "logind.h"
+#include "mkdir.h"
+#include "path-util.h"
#include "process-util.h"
+#include "selinux-util.h"
+#include "sleep-config.h"
+#include "special.h"
+#include "strv.h"
#include "terminal-util.h"
+#include "udev-util.h"
+#include "unit-name.h"
+#include "user-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;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
Session *session;
int r;
@@ -81,7 +88,7 @@ int manager_get_user_from_creds(Manager *m, sd_bus_message *message, uid_t uid,
assert(ret);
if (uid == UID_INVALID) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
/* Note that we get the owner UID of the session, not the actual client UID here! */
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds);
@@ -117,7 +124,6 @@ int manager_get_seat_from_creds(Manager *m, sd_bus_message *message, const char
return r;
seat = session->seat;
-
if (!seat)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SEAT, "Session has no seat.");
} else {
@@ -301,8 +307,10 @@ static int method_get_session_by_pid(sd_bus_message *message, void *userdata, sd
r = sd_bus_message_read(message, "u", &pid);
if (r < 0)
return r;
+ if (pid < 0)
+ return -EINVAL;
- if (pid <= 0) {
+ if (pid == 0) {
r = manager_get_session_from_creds(m, message, NULL, error, &session);
if (r < 0)
return r;
@@ -362,8 +370,10 @@ static int method_get_user_by_pid(sd_bus_message *message, void *userdata, sd_bu
r = sd_bus_message_read(message, "u", &pid);
if (r < 0)
return r;
+ if (pid < 0)
+ return -EINVAL;
- if (pid <= 0) {
+ if (pid == 0) {
r = manager_get_user_from_creds(m, message, UID_INVALID, error, &user);
if (r < 0)
return r;
@@ -408,7 +418,7 @@ static int method_get_seat(sd_bus_message *message, void *userdata, sd_bus_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;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
Manager *m = userdata;
Session *session;
Iterator i;
@@ -450,7 +460,7 @@ static int method_list_sessions(sd_bus_message *message, void *userdata, sd_bus_
}
static int method_list_users(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
Manager *m = userdata;
User *user;
Iterator i;
@@ -490,7 +500,7 @@ static int method_list_users(sd_bus_message *message, void *userdata, sd_bus_err
}
static int method_list_seats(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
Manager *m = userdata;
Seat *seat;
Iterator i;
@@ -527,7 +537,7 @@ static int method_list_seats(sd_bus_message *message, void *userdata, sd_bus_err
}
static int method_list_inhibitors(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
Manager *m = userdata;
Inhibitor *inhibitor;
Iterator i;
@@ -566,12 +576,14 @@ static int method_list_inhibitors(sd_bus_message *message, void *userdata, sd_bu
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;
+ uint32_t audit_id = 0;
_cleanup_free_ char *id = NULL;
Session *session = NULL;
Manager *m = userdata;
User *user = NULL;
Seat *seat = NULL;
+ pid_t leader;
+ uid_t uid;
int remote;
uint32_t vtnr = 0;
SessionType t;
@@ -581,11 +593,16 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
assert(message);
assert(m);
+ assert_cc(sizeof(pid_t) == sizeof(uint32_t));
+ assert_cc(sizeof(uid_t) == sizeof(uint32_t));
+
r = sd_bus_message_read(message, "uusssssussbss", &uid, &leader, &service, &type, &class, &desktop, &cseat, &vtnr, &tty, &display, &remote, &remote_user, &remote_host);
if (r < 0)
return r;
- if (leader == 1)
+ if (!uid_is_valid(uid))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid UID");
+ if (leader < 0 || leader == 1)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid leader PID");
if (isempty(type))
@@ -677,8 +694,8 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
c = SESSION_USER;
}
- if (leader <= 0) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ if (leader == 0) {
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
if (r < 0)
@@ -1076,7 +1093,7 @@ static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bu
return r;
if (uid == UID_INVALID) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
/* Note that we get the owner UID of the session, not the actual client UID here! */
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds);
@@ -1086,12 +1103,14 @@ static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bu
r = sd_bus_creds_get_owner_uid(creds, &uid);
if (r < 0)
return r;
- }
+
+ } else if (!uid_is_valid(uid))
+ return -EINVAL;
errno = 0;
pw = getpwuid(uid);
if (!pw)
- return errno ? -errno : -ENOENT;
+ return errno > 0 ? -errno : -ENOENT;
r = bus_verify_polkit_async(
message,
@@ -1352,24 +1371,26 @@ static int bus_manager_log_shutdown(
return 0;
if (streq(unit_name, SPECIAL_POWEROFF_TARGET)) {
- p = "MESSAGE=System is powering down.";
+ p = "MESSAGE=System is powering down";
q = "SHUTDOWN=power-off";
} else if (streq(unit_name, SPECIAL_HALT_TARGET)) {
- p = "MESSAGE=System is halting.";
+ p = "MESSAGE=System is halting";
q = "SHUTDOWN=halt";
} else if (streq(unit_name, SPECIAL_REBOOT_TARGET)) {
- p = "MESSAGE=System is rebooting.";
+ p = "MESSAGE=System is rebooting";
q = "SHUTDOWN=reboot";
} else if (streq(unit_name, SPECIAL_KEXEC_TARGET)) {
- p = "MESSAGE=System is rebooting with kexec.";
+ p = "MESSAGE=System is rebooting with kexec";
q = "SHUTDOWN=kexec";
} else {
- p = "MESSAGE=System is shutting down.";
+ p = "MESSAGE=System is shutting down";
q = NULL;
}
- if (!isempty(m->wall_message))
- p = strjoina(p, " (", m->wall_message, ")");
+ if (isempty(m->wall_message))
+ p = strjoina(p, ".");
+ else
+ p = strjoina(p, " (", m->wall_message, ").");
return log_struct(LOG_NOTICE,
LOG_MESSAGE_ID(SD_MESSAGE_SHUTDOWN),
@@ -1443,7 +1464,7 @@ static int execute_shutdown_or_sleep(
const char *unit_name,
sd_bus_error *error) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
char *c = NULL;
const char *p;
int r;
@@ -1493,7 +1514,7 @@ static int execute_shutdown_or_sleep(
int manager_dispatch_delayed(Manager *manager, bool timeout) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
Inhibitor *offending = NULL;
int r;
@@ -1646,7 +1667,7 @@ static int verify_shutdown_creds(
const char *action_ignore_inhibit,
sd_bus_error *error) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
bool multiple_sessions, blocked;
uid_t uid;
int r;
@@ -1798,7 +1819,7 @@ static int nologin_timeout_handler(
log_info("Creating /run/nologin, blocking further logins...");
- r = write_string_file("/run/nologin", "System is going down.", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
+ r = write_string_file_atomic_label("/run/nologin", "System is going down.");
if (r < 0)
log_error_errno(r, "Failed to create /run/nologin: %m");
else
@@ -1867,7 +1888,7 @@ static int manager_scheduled_shutdown_handler(
uint64_t usec,
void *userdata) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
Manager *m = userdata;
const char *target;
int r;
@@ -1893,7 +1914,7 @@ static int manager_scheduled_shutdown_handler(
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;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
const char *action_multiple_sessions = NULL;
const char *action_ignore_inhibit = NULL;
const char *action = NULL;
@@ -1922,9 +1943,9 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_
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";
+ action = "org.freedesktop.login1.power-off";
+ action_multiple_sessions = "org.freedesktop.login1.power-off-multiple-sessions";
+ action_ignore_inhibit = "org.freedesktop.login1.power-off-ignore-inhibit";
} else
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unsupported shutdown type");
@@ -1973,7 +1994,7 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_
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;
+ const char *tty = NULL;
(void) sd_bus_creds_get_uid(creds, &m->scheduled_shutdown_uid);
(void) sd_bus_creds_get_tty(creds, &tty);
@@ -2010,7 +2031,7 @@ static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userd
reset_scheduled_shutdown(m);
if (cancelled) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
const char *tty = NULL;
uid_t uid = 0;
int r;
@@ -2022,7 +2043,7 @@ static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userd
}
utmp_wall("The system shutdown has been cancelled",
- lookup_uid(uid), tty, logind_wall_tty_filter, m);
+ uid_to_name(uid), tty, logind_wall_tty_filter, m);
}
return sd_bus_reply_method_return(message, "b", cancelled);
@@ -2066,7 +2087,7 @@ static int method_can_shutdown_or_sleep(
const char *sleep_verb,
sd_bus_error *error) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
bool multiple_sessions, challenge, blocked;
const char *result = NULL;
uid_t uid;
@@ -2352,7 +2373,7 @@ static int method_set_wall_message(
}
static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
const char *who, *why, *what, *mode;
_cleanup_free_ char *id = NULL;
_cleanup_close_ int fifo_fd = -1;
@@ -2559,7 +2580,7 @@ static int session_jobs_reply(Session *s, const char *unit, const char *result)
if (streq(result, "done"))
r = session_send_create_reply(s, NULL);
else {
- _cleanup_bus_error_free_ sd_bus_error e = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL;
sd_bus_error_setf(&e, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result);
r = session_send_create_reply(s, &e);
@@ -2586,7 +2607,7 @@ int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *err
}
if (m->action_job && streq(m->action_job, path)) {
- log_info("Operation finished.");
+ log_info("Operation '%s' finished.", inhibit_what_to_string(m->action_what));
/* Tell people that they now may take a lock again */
send_prepare_for(m, m->action_what, false);
@@ -2598,11 +2619,8 @@ int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *err
}
session = hashmap_get(m->session_units, unit);
- if (session) {
-
- if (streq_ptr(path, session->scope_job))
- session->scope_job = mfree(session->scope_job);
-
+ if (session && streq_ptr(path, session->scope_job)) {
+ session->scope_job = mfree(session->scope_job);
session_jobs_reply(session, unit, result);
session_save(session);
@@ -2611,7 +2629,9 @@ int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *err
}
user = hashmap_get(m->user_units, unit);
- if (user) {
+ if (user &&
+ (streq_ptr(path, user->service_job) ||
+ streq_ptr(path, user->slice_job))) {
if (streq_ptr(path, user->service_job))
user->service_job = mfree(user->service_job);
@@ -2731,22 +2751,114 @@ int manager_send_changed(Manager *manager, const char *property, ...) {
l);
}
+static int strdup_job(sd_bus_message *reply, char **job) {
+ const char *j;
+ char *copy;
+ int r;
+
+ r = sd_bus_message_read(reply, "o", &j);
+ if (r < 0)
+ return r;
+
+ copy = strdup(j);
+ if (!copy)
+ return -ENOMEM;
+
+ *job = copy;
+ return 1;
+}
+
+int manager_start_slice(
+ Manager *manager,
+ const char *slice,
+ const char *description,
+ const char *after,
+ const char *after2,
+ uint64_t tasks_max,
+ sd_bus_error *error,
+ char **job) {
+
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
+ int r;
+
+ assert(manager);
+ assert(slice);
+ assert(job);
+
+ r = sd_bus_message_new_method_call(
+ manager->bus,
+ &m,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "StartTransientUnit");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(m, "ss", strempty(slice), "fail");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(m, 'a', "(sv)");
+ if (r < 0)
+ return r;
+
+ if (!isempty(description)) {
+ r = sd_bus_message_append(m, "(sv)", "Description", "s", description);
+ if (r < 0)
+ return r;
+ }
+
+ if (!isempty(after)) {
+ r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after);
+ if (r < 0)
+ return r;
+ }
+
+ if (!isempty(after2)) {
+ r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after2);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", tasks_max);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(m, "a(sa(sv))", 0);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_call(manager->bus, m, 0, error, &reply);
+ if (r < 0)
+ return r;
+
+ return strdup_job(reply, job);
+}
+
int manager_start_scope(
Manager *manager,
const char *scope,
pid_t pid,
const char *slice,
const char *description,
- const char *after, const char *after2,
+ const char *after,
+ const char *after2,
+ uint64_t tasks_max,
sd_bus_error *error,
char **job) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
int r;
assert(manager);
assert(scope);
assert(pid > 1);
+ assert(job);
r = sd_bus_message_new_method_call(
manager->bus,
@@ -2805,6 +2917,10 @@ int manager_start_scope(
if (r < 0)
return r;
+ r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", tasks_max);
+ if (r < 0)
+ return r;
+
r = sd_bus_message_close_container(m);
if (r < 0)
return r;
@@ -2817,30 +2933,16 @@ int manager_start_scope(
if (r < 0)
return r;
- if (job) {
- const char *j;
- char *copy;
-
- r = sd_bus_message_read(reply, "o", &j);
- if (r < 0)
- return r;
-
- copy = strdup(j);
- if (!copy)
- return -ENOMEM;
-
- *job = copy;
- }
-
- return 1;
+ return strdup_job(reply, job);
}
int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
int r;
assert(manager);
assert(unit);
+ assert(job);
r = sd_bus_call_method(
manager->bus,
@@ -2850,34 +2952,20 @@ int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error,
"StartUnit",
error,
&reply,
- "ss", unit, "fail");
+ "ss", unit, "replace");
if (r < 0)
return r;
- if (job) {
- const char *j;
- char *copy;
-
- r = sd_bus_message_read(reply, "o", &j);
- if (r < 0)
- return r;
-
- copy = strdup(j);
- if (!copy)
- return -ENOMEM;
-
- *job = copy;
- }
-
- return 1;
+ return strdup_job(reply, job);
}
int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
int r;
assert(manager);
assert(unit);
+ assert(job);
r = sd_bus_call_method(
manager->bus,
@@ -2892,9 +2980,7 @@ int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, c
if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) ||
sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) {
- if (job)
- *job = NULL;
-
+ *job = NULL;
sd_bus_error_free(error);
return 0;
}
@@ -2902,22 +2988,7 @@ int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, c
return r;
}
- if (job) {
- const char *j;
- char *copy;
-
- r = sd_bus_message_read(reply, "o", &j);
- if (r < 0)
- return r;
-
- copy = strdup(j);
- if (!copy)
- return -ENOMEM;
-
- *job = copy;
- }
-
- return 1;
+ return strdup_job(reply, job);
}
int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *error) {
@@ -2970,8 +3041,8 @@ int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo
}
int manager_unit_is_active(Manager *manager, const char *unit) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_free_ char *path = NULL;
const char *state;
int r;
@@ -3016,8 +3087,8 @@ int manager_unit_is_active(Manager *manager, const char *unit) {
}
int manager_job_is_active(Manager *manager, const char *path) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
int r;
assert(manager);
diff --git a/src/login/logind-device.c b/src/login/logind-device.c
index ee4c45fb8d..ffb9162e56 100644
--- a/src/login/logind-device.c
+++ b/src/login/logind-device.c
@@ -21,8 +21,9 @@
#include <string.h>
-#include "util.h"
+#include "alloc-util.h"
#include "logind-device.h"
+#include "util.h"
Device* device_new(Manager *m, const char *sysfs, bool master) {
Device *d;
diff --git a/src/login/logind-gperf.gperf b/src/login/logind-gperf.gperf
index 9218d098e0..8552c464cc 100644
--- a/src/login/logind-gperf.gperf
+++ b/src/login/logind-gperf.gperf
@@ -34,3 +34,4 @@ Login.IdleAction, config_parse_handle_action, 0, offsetof(Manag
Login.IdleActionSec, config_parse_sec, 0, offsetof(Manager, idle_action_usec)
Login.RuntimeDirectorySize, config_parse_tmpfs_size, 0, offsetof(Manager, runtime_dir_size)
Login.RemoveIPC, config_parse_bool, 0, offsetof(Manager, remove_ipc)
+Login.UserTasksMax, config_parse_uint64, 0, offsetof(Manager, user_tasks_max)
diff --git a/src/login/logind-inhibit.c b/src/login/logind-inhibit.c
index 0c9c1e5e97..33fbdde557 100644
--- a/src/login/logind-inhibit.c
+++ b/src/login/logind-inhibit.c
@@ -24,11 +24,18 @@
#include <string.h>
#include <unistd.h>
-#include "util.h"
-#include "mkdir.h"
-#include "logind-inhibit.h"
+#include "alloc-util.h"
+#include "escape.h"
+#include "fd-util.h"
#include "fileio.h"
#include "formats-util.h"
+#include "logind-inhibit.h"
+#include "mkdir.h"
+#include "parse-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "user-util.h"
+#include "util.h"
Inhibitor* inhibitor_new(Manager *m, const char* id) {
Inhibitor *i;
diff --git a/src/login/logind-seat-dbus.c b/src/login/logind-seat-dbus.c
index 346e1d2cec..e7dccbdba4 100644
--- a/src/login/logind-seat-dbus.c
+++ b/src/login/logind-seat-dbus.c
@@ -22,13 +22,15 @@
#include <errno.h>
#include <string.h>
-#include "util.h"
-#include "bus-util.h"
-#include "strv.h"
+#include "alloc-util.h"
#include "bus-common-errors.h"
#include "bus-label.h"
-#include "logind.h"
+#include "bus-util.h"
#include "logind-seat.h"
+#include "logind.h"
+#include "strv.h"
+#include "user-util.h"
+#include "util.h"
static int property_get_active_session(
sd_bus *bus,
@@ -332,7 +334,7 @@ int seat_object_find(sd_bus *bus, const char *path, const char *interface, void
assert(m);
if (streq(path, "/org/freedesktop/login1/seat/self")) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
sd_bus_message *message;
Session *session;
const char *name;
@@ -414,7 +416,7 @@ int seat_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***
message = sd_bus_get_current_message(bus);
if (message) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
const char *name;
Session *session;
diff --git a/src/login/logind-seat.c b/src/login/logind-seat.c
index 8d13a63688..9d111f737c 100644
--- a/src/login/logind-seat.c
+++ b/src/login/logind-seat.c
@@ -20,17 +20,24 @@
***/
#include <errno.h>
-#include <unistd.h>
#include <fcntl.h>
#include <string.h>
+#include <unistd.h>
#include "sd-messages.h"
-#include "logind-seat.h"
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "formats-util.h"
#include "logind-acl.h"
-#include "util.h"
+#include "logind-seat.h"
#include "mkdir.h"
-#include "formats-util.h"
+#include "parse-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
#include "terminal-util.h"
+#include "util.h"
Seat *seat_new(Manager *m, const char *id) {
Seat *s;
@@ -175,7 +182,7 @@ static int vt_allocate(unsigned int vtnr) {
assert(vtnr >= 1);
- snprintf(p, sizeof(p), "/dev/tty%u", vtnr);
+ xsprintf(p, "/dev/tty%u", vtnr);
fd = open_terminal(p, O_RDWR|O_NOCTTY|O_CLOEXEC);
if (fd < 0)
return -errno;
diff --git a/src/login/logind-session-dbus.c b/src/login/logind-session-dbus.c
index e6b4ccd7c6..99438d87f7 100644
--- a/src/login/logind-session-dbus.c
+++ b/src/login/logind-session-dbus.c
@@ -22,15 +22,16 @@
#include <errno.h>
#include <string.h>
-#include "util.h"
-#include "strv.h"
-#include "bus-util.h"
+#include "alloc-util.h"
#include "bus-common-errors.h"
#include "bus-label.h"
-
-#include "logind.h"
-#include "logind-session.h"
+#include "bus-util.h"
+#include "fd-util.h"
#include "logind-session-device.h"
+#include "logind-session.h"
+#include "logind.h"
+#include "strv.h"
+#include "util.h"
static int property_get_user(
sd_bus *bus,
@@ -251,7 +252,7 @@ int bus_session_method_lock(sd_bus_message *message, void *userdata, sd_bus_erro
}
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;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
Session *s = userdata;
uid_t uid;
int r, b;
@@ -326,7 +327,7 @@ int bus_session_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro
}
static int method_take_control(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
Session *s = userdata;
int r, force;
uid_t uid;
@@ -520,7 +521,7 @@ int session_object_find(sd_bus *bus, const char *path, const char *interface, vo
assert(m);
if (streq(path, "/org/freedesktop/login1/session/self")) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
sd_bus_message *message;
const char *name;
@@ -597,7 +598,7 @@ int session_node_enumerator(sd_bus *bus, const char *path, void *userdata, char
message = sd_bus_get_current_message(bus);
if (message) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
const char *name;
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds);
@@ -691,7 +692,7 @@ int session_send_lock_all(Manager *m, bool lock) {
}
int session_send_create_reply(Session *s, sd_bus_error *error) {
- _cleanup_bus_message_unref_ sd_bus_message *c = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL;
_cleanup_close_ int fifo_fd = -1;
_cleanup_free_ char *p = NULL;
diff --git a/src/login/logind-session-device.c b/src/login/logind-session-device.c
index 656f268dba..adec894da2 100644
--- a/src/login/logind-session-device.c
+++ b/src/login/logind-session-device.c
@@ -20,16 +20,19 @@
***/
#include <fcntl.h>
-#include <libudev.h>
#include <linux/input.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
-#include "util.h"
-#include "missing.h"
+#include "libudev.h"
+
+#include "alloc-util.h"
#include "bus-util.h"
+#include "fd-util.h"
#include "logind-session-device.h"
+#include "missing.h"
+#include "util.h"
enum SessionDeviceNotifications {
SESSION_DEVICE_RESUME,
@@ -39,7 +42,7 @@ enum SessionDeviceNotifications {
};
static int session_device_notify(SessionDevice *sd, enum SessionDeviceNotifications type) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *path = NULL;
const char *t = NULL;
uint32_t major, minor;
diff --git a/src/login/logind-session.c b/src/login/logind-session.c
index f5fe030b07..c8d505e773 100644
--- a/src/login/logind-session.c
+++ b/src/login/logind-session.c
@@ -21,24 +21,32 @@
#include <errno.h>
#include <fcntl.h>
-#include <linux/vt.h>
#include <linux/kd.h>
+#include <linux/vt.h>
#include <signal.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include "sd-messages.h"
-#include "util.h"
-#include "mkdir.h"
-#include "path-util.h"
-#include "fileio.h"
-#include "audit.h"
-#include "bus-util.h"
+
+#include "alloc-util.h"
+#include "audit-util.h"
#include "bus-error.h"
-#include "logind-session.h"
+#include "bus-util.h"
+#include "escape.h"
+#include "fd-util.h"
+#include "fileio.h"
#include "formats-util.h"
+#include "io-util.h"
+#include "logind-session.h"
+#include "mkdir.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "string-table.h"
#include "terminal-util.h"
+#include "user-util.h"
+#include "util.h"
#define RELEASE_USEC (20*USEC_PER_SEC)
@@ -504,25 +512,31 @@ static int session_start_scope(Session *s) {
assert(s);
assert(s->user);
- assert(s->user->slice);
if (!s->scope) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_free_ char *description = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
char *scope, *job = NULL;
-
- description = strjoin("Session ", s->id, " of user ", s->user->name, NULL);
- if (!description)
- return log_oom();
+ const char *description;
scope = strjoin("session-", s->id, ".scope", NULL);
if (!scope)
return log_oom();
- r = manager_start_scope(s->manager, scope, s->leader, s->user->slice, description, "systemd-logind.service", "systemd-user-sessions.service", &error, &job);
+ description = strjoina("Session ", s->id, " of user ", s->user->name, NULL);
+
+ r = manager_start_scope(
+ s->manager,
+ scope,
+ s->leader,
+ s->user->slice,
+ description,
+ "systemd-logind.service",
+ "systemd-user-sessions.service",
+ (uint64_t) -1, /* disable TasksMax= for the scope, rely on the slice setting for it */
+ &error,
+ &job);
if (r < 0) {
- log_error("Failed to start session scope %s: %s %s",
- scope, bus_error_message(&error, r), error.name);
+ log_error_errno(r, "Failed to start session scope %s: %s", scope, bus_error_message(&error, r));
free(scope);
return r;
} else {
@@ -534,7 +548,7 @@ static int session_start_scope(Session *s) {
}
if (s->scope)
- hashmap_put(s->manager->session_units, s->scope, s);
+ (void) hashmap_put(s->manager->session_units, s->scope, s);
return 0;
}
@@ -597,7 +611,7 @@ int session_start(Session *s) {
}
static int session_stop_scope(Session *s, bool force) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
char *job = NULL;
int r;
@@ -987,7 +1001,7 @@ static int session_open_vt(Session *s) {
sprintf(path, "/dev/tty%u", s->vtnr);
s->vtfd = open_terminal(path, O_RDWR | O_CLOEXEC | O_NONBLOCK | O_NOCTTY);
if (s->vtfd < 0)
- return log_error_errno(errno, "cannot open VT %s of session %s: %m", path, s->id);
+ return log_error_errno(s->vtfd, "cannot open VT %s of session %s: %m", path, s->id);
return s->vtfd;
}
@@ -1049,9 +1063,13 @@ error:
}
void session_restore_vt(Session *s) {
+
+ static const struct vt_mode mode = {
+ .mode = VT_AUTO,
+ };
+
_cleanup_free_ char *utf8 = NULL;
- int vt, kb = K_XLATE;
- struct vt_mode mode = { 0 };
+ int vt, kb, old_fd;
/* We need to get a fresh handle to the virtual terminal,
* since the old file-descriptor is potentially in a hung-up
@@ -1059,7 +1077,7 @@ void session_restore_vt(Session *s) {
* little dance to avoid having the terminal be available
* for reuse before we've cleaned it up.
*/
- int old_fd = s->vtfd;
+ old_fd = s->vtfd;
s->vtfd = -1;
vt = session_open_vt(s);
@@ -1072,13 +1090,13 @@ void session_restore_vt(Session *s) {
if (read_one_line_file("/sys/module/vt/parameters/default_utf8", &utf8) >= 0 && *utf8 == '1')
kb = K_UNICODE;
+ else
+ kb = K_XLATE;
(void) ioctl(vt, KDSKBMODE, kb);
- mode.mode = VT_AUTO;
(void) ioctl(vt, VT_SETMODE, &mode);
-
- fchown(vt, 0, -1);
+ (void) fchown(vt, 0, (gid_t) -1);
s->vtfd = safe_close(s->vtfd);
}
diff --git a/src/login/logind-session.h b/src/login/logind-session.h
index d054c33cec..d27407fc92 100644
--- a/src/login/logind-session.h
+++ b/src/login/logind-session.h
@@ -25,8 +25,8 @@ typedef struct Session Session;
typedef enum KillWho KillWho;
#include "list.h"
-#include "logind-user.h"
#include "login-util.h"
+#include "logind-user.h"
typedef enum SessionState {
SESSION_OPENING, /* Session scope is being created */
diff --git a/src/login/logind-user-dbus.c b/src/login/logind-user-dbus.c
index 20ea2fbdc4..3a27305488 100644
--- a/src/login/logind-user-dbus.c
+++ b/src/login/logind-user-dbus.c
@@ -22,11 +22,13 @@
#include <errno.h>
#include <string.h>
-#include "strv.h"
+#include "alloc-util.h"
#include "bus-util.h"
-#include "logind.h"
-#include "logind-user.h"
#include "formats-util.h"
+#include "logind-user.h"
+#include "logind.h"
+#include "strv.h"
+#include "user-util.h"
static int property_get_display(
sd_bus *bus,
@@ -269,7 +271,7 @@ int user_object_find(sd_bus *bus, const char *path, const char *interface, void
assert(m);
if (streq(path, "/org/freedesktop/login1/user/self")) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
sd_bus_message *message;
message = sd_bus_get_current_message(bus);
@@ -338,7 +340,7 @@ int user_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***
message = sd_bus_get_current_message(bus);
if (message) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
uid_t uid;
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds);
diff --git a/src/login/logind-user.c b/src/login/logind-user.c
index 47669afdef..98f8ea3c78 100644
--- a/src/login/logind-user.c
+++ b/src/login/logind-user.c
@@ -19,64 +19,94 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/mount.h>
+#include <errno.h>
#include <string.h>
+#include <sys/mount.h>
#include <unistd.h>
-#include <errno.h>
-#include "util.h"
-#include "mkdir.h"
-#include "rm-rf.h"
-#include "hashmap.h"
-#include "fileio.h"
-#include "path-util.h"
-#include "special.h"
-#include "unit-name.h"
-#include "bus-util.h"
+#include "alloc-util.h"
+#include "bus-common-errors.h"
#include "bus-error.h"
-#include "conf-parser.h"
+#include "bus-util.h"
#include "clean-ipc.h"
-#include "smack-util.h"
+#include "conf-parser.h"
+#include "escape.h"
+#include "fd-util.h"
+#include "fileio.h"
#include "formats-util.h"
+#include "fs-util.h"
+#include "hashmap.h"
#include "label.h"
#include "logind-user.h"
+#include "mkdir.h"
+#include "mount-util.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "rm-rf.h"
+#include "smack-util.h"
+#include "special.h"
+#include "stdio-util.h"
+#include "string-table.h"
+#include "unit-name.h"
+#include "user-util.h"
+#include "util.h"
-User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
- User *u;
+int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name) {
+ _cleanup_(user_freep) User *u = NULL;
+ char lu[DECIMAL_STR_MAX(uid_t) + 1];
+ int r;
+ assert(out);
assert(m);
assert(name);
u = new0(User, 1);
if (!u)
- return NULL;
+ return -ENOMEM;
+
+ u->manager = m;
+ u->uid = uid;
+ u->gid = gid;
+ xsprintf(lu, UID_FMT, uid);
u->name = strdup(name);
if (!u->name)
- goto fail;
+ return -ENOMEM;
if (asprintf(&u->state_file, "/run/systemd/users/"UID_FMT, uid) < 0)
- goto fail;
+ return -ENOMEM;
- if (hashmap_put(m->users, UID_TO_PTR(uid), u) < 0)
- goto fail;
+ if (asprintf(&u->runtime_path, "/run/user/"UID_FMT, uid) < 0)
+ return -ENOMEM;
- u->manager = m;
- u->uid = uid;
- u->gid = gid;
+ r = slice_build_subslice(SPECIAL_USER_SLICE, lu, &u->slice);
+ if (r < 0)
+ return r;
- return u;
+ r = unit_name_build("user", lu, ".service", &u->service);
+ if (r < 0)
+ return r;
-fail:
- free(u->state_file);
- free(u->name);
- free(u);
+ r = hashmap_put(m->users, UID_TO_PTR(uid), u);
+ if (r < 0)
+ return r;
+
+ r = hashmap_put(m->user_units, u->slice, u);
+ if (r < 0)
+ return r;
+
+ r = hashmap_put(m->user_units, u->service, u);
+ if (r < 0)
+ return r;
- return NULL;
+ *out = u;
+ u = NULL;
+ return 0;
}
-void user_free(User *u) {
- assert(u);
+User *user_free(User *u) {
+ if (!u)
+ return NULL;
if (u->in_gc_queue)
LIST_REMOVE(gc_queue, u->manager->user_gc_queue, u);
@@ -84,26 +114,24 @@ void user_free(User *u) {
while (u->sessions)
session_free(u->sessions);
- if (u->slice) {
- hashmap_remove(u->manager->user_units, u->slice);
- free(u->slice);
- }
+ if (u->service)
+ hashmap_remove_value(u->manager->user_units, u->service, u);
- if (u->service) {
- hashmap_remove(u->manager->user_units, u->service);
- free(u->service);
- }
+ if (u->slice)
+ hashmap_remove_value(u->manager->user_units, u->slice, u);
- free(u->slice_job);
- free(u->service_job);
+ hashmap_remove_value(u->manager->users, UID_TO_PTR(u->uid), u);
- free(u->runtime_path);
+ u->slice_job = mfree(u->slice_job);
+ u->service_job = mfree(u->service_job);
- hashmap_remove(u->manager->users, UID_TO_PTR(u->uid));
+ u->service = mfree(u->service);
+ u->slice = mfree(u->slice);
+ u->runtime_path = mfree(u->runtime_path);
+ u->state_file = mfree(u->state_file);
+ u->name = mfree(u->name);
- free(u->name);
- free(u->state_file);
- free(u);
+ return mfree(u);
}
static int user_save_internal(User *u) {
@@ -131,16 +159,13 @@ static int user_save_internal(User *u) {
u->name,
user_state_to_string(user_get_state(u)));
+ /* LEGACY: no-one reads RUNTIME= anymore, drop it at some point */
if (u->runtime_path)
fprintf(f, "RUNTIME=%s\n", u->runtime_path);
- if (u->service)
- fprintf(f, "SERVICE=%s\n", u->service);
if (u->service_job)
fprintf(f, "SERVICE_JOB=%s\n", u->service_job);
- if (u->slice)
- fprintf(f, "SLICE=%s\n", u->slice);
if (u->slice_job)
fprintf(f, "SLICE_JOB=%s\n", u->slice_job);
@@ -278,10 +303,7 @@ int user_load(User *u) {
assert(u);
r = parse_env_file(u->state_file, NEWLINE,
- "RUNTIME", &u->runtime_path,
- "SERVICE", &u->service,
"SERVICE_JOB", &u->service_job,
- "SLICE", &u->slice,
"SLICE_JOB", &u->slice_job,
"DISPLAY", &display,
"REALTIME", &realtime,
@@ -317,7 +339,6 @@ int user_load(User *u) {
}
static int user_mkdir_runtime_path(User *u) {
- char *p;
int r;
assert(u);
@@ -326,16 +347,10 @@ static int user_mkdir_runtime_path(User *u) {
if (r < 0)
return log_error_errno(r, "Failed to create /run/user: %m");
- if (!u->runtime_path) {
- if (asprintf(&p, "/run/user/" UID_FMT, u->uid) < 0)
- return log_oom();
- } else
- p = u->runtime_path;
-
- if (path_is_mount_point(p, 0) <= 0) {
+ if (path_is_mount_point(u->runtime_path, 0) <= 0) {
_cleanup_free_ char *t = NULL;
- (void) mkdir_label(p, 0700);
+ (void) mkdir_label(u->runtime_path, 0700);
if (mac_smack_use())
r = asprintf(&t, "mode=0700,smackfsroot=*,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size);
@@ -346,10 +361,10 @@ static int user_mkdir_runtime_path(User *u) {
goto fail;
}
- r = mount("tmpfs", p, "tmpfs", MS_NODEV|MS_NOSUID, t);
+ r = mount("tmpfs", u->runtime_path, "tmpfs", MS_NODEV|MS_NOSUID, t);
if (r < 0) {
if (errno != EPERM) {
- r = log_error_errno(errno, "Failed to mount per-user tmpfs directory %s: %m", p);
+ r = log_error_errno(errno, "Failed to mount per-user tmpfs directory %s: %m", u->runtime_path);
goto fail;
}
@@ -357,95 +372,77 @@ static int user_mkdir_runtime_path(User *u) {
* CAP_SYS_ADMIN-less container? In this case,
* just use a normal directory. */
- r = chmod_and_chown(p, 0700, u->uid, u->gid);
+ r = chmod_and_chown(u->runtime_path, 0700, u->uid, u->gid);
if (r < 0) {
log_error_errno(r, "Failed to change runtime directory ownership and mode: %m");
goto fail;
}
}
- r = label_fix(p, false, false);
+ r = label_fix(u->runtime_path, false, false);
if (r < 0)
- log_warning_errno(r, "Failed to fix label of '%s', ignoring: %m", p);
+ log_warning_errno(r, "Failed to fix label of '%s', ignoring: %m", u->runtime_path);
}
- u->runtime_path = p;
return 0;
fail:
- if (p) {
- /* Try to clean up, but ignore errors */
- (void) rmdir(p);
- free(p);
- }
-
- u->runtime_path = NULL;
+ /* Try to clean up, but ignore errors */
+ (void) rmdir(u->runtime_path);
return r;
}
static int user_start_slice(User *u) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ const char *description;
char *job;
int r;
assert(u);
- if (!u->slice) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- char lu[DECIMAL_STR_MAX(uid_t) + 1], *slice;
- sprintf(lu, UID_FMT, u->uid);
-
- r = slice_build_subslice(SPECIAL_USER_SLICE, lu, &slice);
- if (r < 0)
- return r;
-
- r = manager_start_unit(u->manager, slice, &error, &job);
- if (r < 0) {
- log_error("Failed to start user slice: %s", bus_error_message(&error, r));
- free(slice);
- } else {
- u->slice = slice;
-
- free(u->slice_job);
- u->slice_job = job;
- }
- }
-
- if (u->slice)
- hashmap_put(u->manager->user_units, u->slice, u);
+ u->slice_job = mfree(u->slice_job);
+ description = strjoina("User Slice of ", u->name);
+
+ r = manager_start_slice(
+ u->manager,
+ u->slice,
+ description,
+ "systemd-logind.service",
+ "systemd-user-sessions.service",
+ u->manager->user_tasks_max,
+ &error,
+ &job);
+ if (r >= 0)
+ u->slice_job = job;
+ else if (!sd_bus_error_has_name(&error, BUS_ERROR_UNIT_EXISTS))
+ /* we don't fail due to this, let's try to continue */
+ log_error_errno(r, "Failed to start user slice %s, ignoring: %s (%s)",
+ u->slice, bus_error_message(&error, r), error.name);
return 0;
}
static int user_start_service(User *u) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
char *job;
int r;
assert(u);
- if (!u->service) {
- char lu[DECIMAL_STR_MAX(uid_t) + 1], *service;
- sprintf(lu, UID_FMT, u->uid);
-
- 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) {
- log_error("Failed to start user service: %s", bus_error_message(&error, r));
- free(service);
- } else {
- u->service = service;
+ u->service_job = mfree(u->service_job);
- free(u->service_job);
- u->service_job = job;
- }
+ r = manager_start_unit(
+ u->manager,
+ u->service,
+ &error,
+ &job);
+ if (r < 0) {
+ /* we don't fail due to this, let's try to continue */
+ log_error_errno(r, "Failed to start user service, ignoring: %s", bus_error_message(&error, r));
+ } else {
+ u->service_job = job;
}
- if (u->service)
- hashmap_put(u->manager->user_units, u->service, u);
-
return 0;
}
@@ -454,15 +451,32 @@ int user_start(User *u) {
assert(u);
- if (u->started)
+ if (u->started && !u->stopping)
return 0;
- log_debug("New user %s logged in.", u->name);
-
- /* Make XDG_RUNTIME_DIR */
- r = user_mkdir_runtime_path(u);
- if (r < 0)
- return r;
+ /*
+ * If u->stopping is set, the user is marked for removal and the slice
+ * and service stop-jobs are queued. We have to clear that flag before
+ * queing the start-jobs again. If they succeed, the user object can be
+ * re-used just fine (pid1 takes care of job-ordering and proper
+ * restart), but if they fail, we want to force another user_stop() so
+ * possibly pending units are stopped.
+ * Note that we don't clear u->started, as we have no clue what state
+ * the user is in on failure here. Hence, we pretend the user is
+ * running so it will be properly taken down by GC. However, we clearly
+ * return an error from user_start() in that case, so no further
+ * reference to the user is taken.
+ */
+ u->stopping = false;
+
+ if (!u->started) {
+ log_debug("New user %s logged in.", u->name);
+
+ /* Make XDG_RUNTIME_DIR */
+ r = user_mkdir_runtime_path(u);
+ if (r < 0)
+ return r;
+ }
/* Create cgroup */
r = user_start_slice(u);
@@ -480,29 +494,26 @@ int user_start(User *u) {
if (r < 0)
return r;
- if (!dual_timestamp_is_set(&u->timestamp))
- dual_timestamp_get(&u->timestamp);
-
- u->started = true;
+ if (!u->started) {
+ if (!dual_timestamp_is_set(&u->timestamp))
+ dual_timestamp_get(&u->timestamp);
+ user_send_signal(u, true);
+ u->started = true;
+ }
/* Save new user data */
user_save(u);
- user_send_signal(u, true);
-
return 0;
}
static int user_stop_slice(User *u) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
char *job;
int r;
assert(u);
- if (!u->slice)
- return 0;
-
r = manager_stop_unit(u->manager, u->slice, &error, &job);
if (r < 0) {
log_error("Failed to stop user slice: %s", bus_error_message(&error, r));
@@ -516,15 +527,12 @@ static int user_stop_slice(User *u) {
}
static int user_stop_service(User *u) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
char *job;
int r;
assert(u);
- if (!u->service)
- return 0;
-
r = manager_stop_unit(u->manager, u->service, &error, &job);
if (r < 0) {
log_error("Failed to stop user service: %s", bus_error_message(&error, r));
@@ -542,9 +550,6 @@ static int user_remove_runtime_path(User *u) {
assert(u);
- if (!u->runtime_path)
- return 0;
-
r = rm_rf(u->runtime_path, 0);
if (r < 0)
log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
@@ -560,8 +565,6 @@ static int user_remove_runtime_path(User *u) {
if (r < 0)
log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
- u->runtime_path = mfree(u->runtime_path);
-
return r;
}
@@ -753,9 +756,6 @@ UserState user_get_state(User *u) {
int user_kill(User *u, int signo) {
assert(u);
- if (!u->slice)
- return -ESRCH;
-
return manager_kill_unit(u->manager, u->slice, KILL_ALL, signo, NULL);
}
@@ -867,13 +867,13 @@ int config_parse_tmpfs_size(
errno = 0;
ul = strtoul(rvalue, &f, 10);
- if (errno != 0 || f != e) {
- log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Failed to parse percentage value, ignoring: %s", rvalue);
+ if (errno > 0 || f != e) {
+ log_syntax(unit, LOG_ERR, filename, line, errno, "Failed to parse percentage value, ignoring: %s", rvalue);
return 0;
}
if (ul <= 0 || ul >= 100) {
- log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Percentage value out of range, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Percentage value out of range, ignoring: %s", rvalue);
return 0;
}
@@ -883,7 +883,7 @@ int config_parse_tmpfs_size(
r = parse_size(rvalue, 1024, &k);
if (r < 0 || (uint64_t) (size_t) k != k) {
- log_syntax(unit, LOG_ERR, filename, line, r < 0 ? r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
return 0;
}
diff --git a/src/login/logind-user.h b/src/login/logind-user.h
index 722247806b..de99cf47b4 100644
--- a/src/login/logind-user.h
+++ b/src/login/logind-user.h
@@ -39,16 +39,13 @@ typedef enum UserState {
struct User {
Manager *manager;
-
uid_t uid;
gid_t gid;
char *name;
-
char *state_file;
char *runtime_path;
-
- char *service;
char *slice;
+ char *service;
char *service_job;
char *slice_job;
@@ -65,8 +62,11 @@ struct User {
LIST_FIELDS(User, gc_queue);
};
-User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name);
-void user_free(User *u);
+int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name);
+User *user_free(User *u);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(User *, user_free);
+
bool user_check_gc(User *u, bool drop_not_started);
void user_add_to_gc_queue(User *u);
int user_start(User *u);
diff --git a/src/login/logind-utmp.c b/src/login/logind-utmp.c
index 1e13ff01de..3bd61a81fd 100644
--- a/src/login/logind-utmp.c
+++ b/src/login/logind-utmp.c
@@ -20,20 +20,23 @@
***/
#include <errno.h>
+#include <pwd.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 "alloc-util.h"
+#include "audit-util.h"
#include "bus-common-errors.h"
-#include "logind.h"
+#include "bus-error.h"
+#include "bus-util.h"
#include "formats-util.h"
+#include "logind.h"
+#include "special.h"
+#include "strv.h"
+#include "unit-name.h"
+#include "user-util.h"
#include "utmp-wtmp.h"
_const_ static usec_t when_wall(usec_t n, usec_t elapse) {
@@ -94,7 +97,7 @@ static int warn_wall(Manager *m, usec_t n) {
return 0;
}
- utmp_wall(l, lookup_uid(m->scheduled_shutdown_uid),
+ utmp_wall(l, uid_to_name(m->scheduled_shutdown_uid),
m->scheduled_shutdown_tty, logind_wall_tty_filter, m);
return 1;
diff --git a/src/login/logind.c b/src/login/logind.c
index 8ac2aceb9b..79ea5ddfcb 100644
--- a/src/login/logind.c
+++ b/src/login/logind.c
@@ -20,20 +20,25 @@
***/
#include <errno.h>
-#include <libudev.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
+#include "libudev.h"
#include "sd-daemon.h"
-#include "strv.h"
-#include "conf-parser.h"
-#include "bus-util.h"
+
+#include "alloc-util.h"
#include "bus-error.h"
-#include "udev-util.h"
+#include "bus-util.h"
+#include "conf-parser.h"
+#include "def.h"
+#include "dirent-util.h"
+#include "fd-util.h"
#include "formats-util.h"
-#include "signal-util.h"
#include "logind.h"
+#include "signal-util.h"
+#include "strv.h"
+#include "udev-util.h"
static void manager_free(Manager *m);
@@ -65,6 +70,7 @@ static Manager *manager_new(void) {
m->idle_action_not_before_usec = now(CLOCK_MONOTONIC);
m->runtime_dir_size = PAGE_ALIGN((size_t) (physical_memory() / 10)); /* 10% */
+ m->user_tasks_max = UINT64_C(4096);
m->devices = hashmap_new(&string_hash_ops);
m->seats = hashmap_new(&string_hash_ops);
@@ -292,8 +298,7 @@ static int manager_enumerate_seats(Manager *m) {
if (errno == ENOENT)
return 0;
- log_error_errno(errno, "Failed to open /run/systemd/seats: %m");
- return -errno;
+ return log_error_errno(errno, "Failed to open /run/systemd/seats: %m");
}
FOREACH_DIRENT(de, d, return -errno) {
@@ -329,8 +334,7 @@ static int manager_enumerate_linger_users(Manager *m) {
if (errno == ENOENT)
return 0;
- log_error_errno(errno, "Failed to open /var/lib/systemd/linger/: %m");
- return -errno;
+ return log_error_errno(errno, "Failed to open /var/lib/systemd/linger/: %m");
}
FOREACH_DIRENT(de, d, return -errno) {
@@ -365,8 +369,7 @@ static int manager_enumerate_users(Manager *m) {
if (errno == ENOENT)
return 0;
- log_error_errno(errno, "Failed to open /run/systemd/users: %m");
- return -errno;
+ return log_error_errno(errno, "Failed to open /run/systemd/users: %m");
}
FOREACH_DIRENT(de, d, return -errno) {
@@ -406,8 +409,7 @@ static int manager_enumerate_sessions(Manager *m) {
if (errno == ENOENT)
return 0;
- log_error_errno(errno, "Failed to open /run/systemd/sessions: %m");
- return -errno;
+ return log_error_errno(errno, "Failed to open /run/systemd/sessions: %m");
}
FOREACH_DIRENT(de, d, return -errno) {
@@ -453,8 +455,7 @@ static int manager_enumerate_inhibitors(Manager *m) {
if (errno == ENOENT)
return 0;
- log_error_errno(errno, "Failed to open /run/systemd/inhibit: %m");
- return -errno;
+ return log_error_errno(errno, "Failed to open /run/systemd/inhibit: %m");
}
FOREACH_DIRENT(de, d, return -errno) {
@@ -578,7 +579,7 @@ static int manager_reserve_vt(Manager *m) {
}
static int manager_connect_bus(Manager *m) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert(m);
@@ -744,8 +745,7 @@ static int manager_connect_console(Manager *m) {
if (errno == ENOENT)
return 0;
- log_error_errno(errno, "Failed to open /sys/class/tty/tty0/active: %m");
- return -errno;
+ return log_error_errno(errno, "Failed to open /sys/class/tty/tty0/active: %m");
}
r = sd_event_add_io(m->event, &m->console_active_event_source, m->console_active_fd, 0, manager_dispatch_console, m);
@@ -1103,8 +1103,8 @@ static int manager_run(Manager *m) {
static int manager_parse_config_file(Manager *m) {
assert(m);
- return config_parse_many("/etc/systemd/logind.conf",
- CONF_DIRS_NULSTR("systemd/logind.conf"),
+ return config_parse_many(PKGSYSCONFDIR "/logind.conf",
+ CONF_PATHS_NULSTR("systemd/logind.conf.d"),
"Login\0",
config_item_perf_lookup, logind_gperf_lookup,
false, m);
diff --git a/src/login/logind.conf b/src/login/logind.conf
index 6df6f04c77..81f6695434 100644
--- a/src/login/logind.conf
+++ b/src/login/logind.conf
@@ -32,3 +32,4 @@
#IdleActionSec=30min
#RuntimeDirectorySize=10%
#RemoveIPC=yes
+#UserTasksMax=4096
diff --git a/src/login/logind.h b/src/login/logind.h
index 7990da5a93..f34544e64c 100644
--- a/src/login/logind.h
+++ b/src/login/logind.h
@@ -22,20 +22,21 @@
***/
#include <stdbool.h>
-#include <libudev.h>
-#include "sd-event.h"
+#include "libudev.h"
#include "sd-bus.h"
-#include "list.h"
+#include "sd-event.h"
+
#include "hashmap.h"
+#include "list.h"
#include "set.h"
typedef struct Manager Manager;
+#include "logind-action.h"
+#include "logind-button.h"
#include "logind-device.h"
#include "logind-inhibit.h"
-#include "logind-button.h"
-#include "logind-action.h"
struct Manager {
sd_event *event;
@@ -133,6 +134,7 @@ struct Manager {
sd_event_source *lid_switch_ignore_event_source;
size_t runtime_dir_size;
+ uint64_t user_tasks_max;
};
int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_device);
@@ -170,7 +172,8 @@ int bus_manager_shutdown_or_sleep_now_or_later(Manager *m, const char *unit_name
int manager_send_changed(Manager *manager, const char *property, ...) _sentinel_;
-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_slice(Manager *manager, const char *slice, const char *description, const char *after, const char *after2, uint64_t tasks_max, sd_bus_error *error, char **job);
+int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, const char *after, const char *after2, uint64_t tasks_max, sd_bus_error *error, char **job);
int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job);
int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job);
int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *error);
diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c
index f66f1ce842..95977426d9 100644
--- a/src/login/pam_systemd.c
+++ b/src/login/pam_systemd.c
@@ -19,31 +19,34 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <endian.h>
#include <errno.h>
#include <fcntl.h>
-#include <sys/file.h>
#include <pwd.h>
-#include <endian.h>
-
-#include <security/pam_modules.h>
#include <security/_pam_macros.h>
-#include <security/pam_modutil.h>
#include <security/pam_ext.h>
#include <security/pam_misc.h>
+#include <security/pam_modules.h>
+#include <security/pam_modutil.h>
+#include <sys/file.h>
+#include "alloc-util.h"
+#include "audit-util.h"
#include "bus-common-errors.h"
-#include "util.h"
-#include "audit.h"
-#include "macro.h"
-#include "strv.h"
+#include "bus-error.h"
#include "bus-util.h"
#include "def.h"
-#include "socket-util.h"
+#include "fd-util.h"
#include "fileio.h"
-#include "bus-error.h"
#include "formats-util.h"
-#include "terminal-util.h"
#include "hostname-util.h"
+#include "login-util.h"
+#include "macro.h"
+#include "parse-util.h"
+#include "socket-util.h"
+#include "strv.h"
+#include "terminal-util.h"
+#include "util.h"
static int parse_argv(
pam_handle_t *handle,
@@ -217,8 +220,8 @@ _public_ PAM_EXTERN int pam_sm_open_session(
int flags,
int argc, const char **argv) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
const char
*username, *id, *object_path, *runtime_path,
*service = NULL,
@@ -227,7 +230,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
*seat = NULL,
*type = NULL, *class = NULL,
*class_pam = NULL, *type_pam = NULL, *cvtnr = NULL, *desktop = NULL;
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int session_fd = -1, existing, r;
bool debug = false, remote;
struct passwd *pw;
@@ -264,29 +267,21 @@ _public_ PAM_EXTERN int pam_sm_open_session(
pam_get_item(handle, PAM_SERVICE, (const void**) &service);
if (streq_ptr(service, "systemd-user")) {
- _cleanup_free_ char *p = NULL, *rt = NULL;
+ _cleanup_free_ char *rt = NULL;
- if (asprintf(&p, "/run/systemd/users/"UID_FMT, pw->pw_uid) < 0)
+ if (asprintf(&rt, "/run/user/"UID_FMT, pw->pw_uid) < 0)
return PAM_BUF_ERR;
- r = parse_env_file(p, NEWLINE,
- "RUNTIME", &rt,
- NULL);
- if (r < 0 && r != -ENOENT)
- return PAM_SESSION_ERR;
-
- if (rt) {
- r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0);
- if (r != PAM_SUCCESS) {
- pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
- return r;
- }
-
- r = export_legacy_dbus_address(handle, pw->pw_uid, rt);
- if (r != PAM_SUCCESS)
- return r;
+ r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0);
+ if (r != PAM_SUCCESS) {
+ pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
+ return r;
}
+ r = export_legacy_dbus_address(handle, pw->pw_uid, rt);
+ if (r != PAM_SUCCESS)
+ return r;
+
return PAM_SUCCESS;
}
@@ -498,7 +493,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
return PAM_SESSION_ERR;
}
- r = pam_set_data(handle, "systemd.session-fd", INT_TO_PTR(session_fd+1), NULL);
+ r = pam_set_data(handle, "systemd.session-fd", FD_TO_PTR(session_fd), NULL);
if (r != PAM_SUCCESS) {
pam_syslog(handle, LOG_ERR, "Failed to install session fd.");
safe_close(session_fd);
@@ -514,8 +509,8 @@ _public_ PAM_EXTERN int pam_sm_close_session(
int flags,
int argc, const char **argv) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
const void *existing = NULL;
const char *id;
int r;
diff --git a/src/login/sysfs-show.c b/src/login/sysfs-show.c
index 9a9fb7622d..e9ca4bb03d 100644
--- a/src/login/sysfs-show.c
+++ b/src/login/sysfs-show.c
@@ -21,13 +21,17 @@
#include <errno.h>
#include <string.h>
-#include <libudev.h>
-#include "util.h"
-#include "sysfs-show.h"
+#include "libudev.h"
+
+#include "alloc-util.h"
+#include "locale-util.h"
#include "path-util.h"
-#include "udev-util.h"
+#include "string-util.h"
+#include "sysfs-show.h"
#include "terminal-util.h"
+#include "udev-util.h"
+#include "util.h"
static int show_sysfs_one(
struct udev *udev,
@@ -114,7 +118,7 @@ static int show_sysfs_one(
"%s%s:%s%s%s%s",
is_master ? "[MASTER] " : "",
subsystem, sysname,
- name ? " \"" : "", name ? name : "", name ? "\"" : "") < 0)
+ name ? " \"" : "", strempty(name), name ? "\"" : "") < 0)
return -ENOMEM;
free(k);
diff --git a/src/login/test-inhibit.c b/src/login/test-inhibit.c
index 03516de916..7022087076 100644
--- a/src/login/test-inhibit.c
+++ b/src/login/test-inhibit.c
@@ -21,14 +21,16 @@
#include <unistd.h>
-#include "macro.h"
-#include "util.h"
#include "sd-bus.h"
+
#include "bus-util.h"
+#include "fd-util.h"
+#include "macro.h"
+#include "util.h"
static int inhibit(sd_bus *bus, const char *what) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
const char *who = "Test Tool", *reason = "Just because!", *mode = "block";
int fd;
int r;
@@ -51,8 +53,8 @@ static int inhibit(sd_bus *bus, const char *what) {
}
static void print_inhibitors(sd_bus *bus) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
const char *what, *who, *why, *mode;
uint32_t uid, pid;
unsigned n = 0;
@@ -83,7 +85,7 @@ static void print_inhibitors(sd_bus *bus) {
}
int main(int argc, char*argv[]) {
- _cleanup_bus_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
int fd1, fd2;
int r;
diff --git a/src/login/test-login-shared.c b/src/login/test-login-shared.c
index 4c4275d124..ac327f71fb 100644
--- a/src/login/test-login-shared.c
+++ b/src/login/test-login-shared.c
@@ -19,8 +19,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "macro.h"
#include "login-util.h"
+#include "macro.h"
static void test_session_id_valid(void) {
assert_se(session_id_valid("c1"));
diff --git a/src/login/test-login-tables.c b/src/login/test-login-tables.c
index a4196bf14b..4fbc893a9a 100644
--- a/src/login/test-login-tables.c
+++ b/src/login/test-login-tables.c
@@ -19,7 +19,6 @@
#include "logind-action.h"
#include "logind-session.h"
-
#include "test-tables.h"
int main(int argc, char **argv) {
diff --git a/src/machine-id-commit/machine-id-commit.c b/src/machine-id-commit/machine-id-commit.c
deleted file mode 100644
index 0f7748e453..0000000000
--- a/src/machine-id-commit/machine-id-commit.c
+++ /dev/null
@@ -1,107 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2014 Didier Roche
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General 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 <stdio.h>
-#include <getopt.h>
-#include <errno.h>
-
-#include "machine-id-setup.h"
-#include "log.h"
-#include "build.h"
-
-static const char *arg_root = NULL;
-
-static void help(void) {
- printf("%s [OPTIONS...]\n\n"
- "Commit a transient /etc/machine-id on disk if writable.\n\n"
- " -h --help Show this help\n"
- " --version Show package version\n"
- " --root=ROOT Filesystem root\n",
- program_invocation_short_name);
-}
-
-static int parse_argv(int argc, char *argv[]) {
-
- enum {
- ARG_VERSION = 0x100,
- ARG_ROOT,
- };
-
- static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, ARG_VERSION },
- { "root", required_argument, NULL, ARG_ROOT },
- {}
- };
-
- int c;
-
- assert(argc >= 0);
- assert(argv);
-
- while ((c = getopt_long(argc, argv, "hqcv", options, NULL)) >= 0)
- switch (c) {
-
- case 'h':
- help();
- return 0;
-
- case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
-
- case ARG_ROOT:
- arg_root = optarg;
- break;
-
- case '?':
- return -EINVAL;
-
- default:
- assert_not_reached("Unhandled option");
- }
-
- if (optind < argc) {
- log_error("Extraneous arguments");
- return -EINVAL;
- }
-
- return 1;
-}
-
-int main(int argc, char *argv[]) {
- int r;
-
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
-
- r = parse_argv(argc, argv);
- if (r <= 0)
- goto finish;
-
- 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 20cb60b804..9d19307236 100644
--- a/src/machine-id-setup/machine-id-setup-main.c
+++ b/src/machine-id-setup/machine-id-setup-main.c
@@ -19,24 +19,27 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
-#include <stdio.h>
-#include <getopt.h>
#include <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
-#include "machine-id-setup.h"
#include "log.h"
-#include "build.h"
+#include "machine-id-setup.h"
+#include "path-util.h"
+#include "util.h"
-static const char *arg_root = "";
+static char *arg_root = NULL;
+static bool arg_commit = false;
static void help(void) {
printf("%s [OPTIONS...]\n\n"
"Initialize /etc/machine-id from a random source.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
- " --root=ROOT Filesystem root\n",
- program_invocation_short_name);
+ " --root=ROOT Filesystem root\n"
+ " --commit Commit transient ID\n"
+ , program_invocation_short_name);
}
static int parse_argv(int argc, char *argv[]) {
@@ -44,16 +47,18 @@ static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_ROOT,
+ ARG_COMMIT,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "root", required_argument, NULL, ARG_ROOT },
+ { "commit", no_argument, NULL, ARG_COMMIT },
{}
};
- int c;
+ int c, r;
assert(argc >= 0);
assert(argv);
@@ -67,12 +72,16 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case ARG_ROOT:
- arg_root = optarg;
+ r = parse_path_argument_and_warn(optarg, true, &arg_root);
+ if (r < 0)
+ return r;
+ break;
+
+ case ARG_COMMIT:
+ arg_commit = true;
break;
case '?':
@@ -98,7 +107,14 @@ int main(int argc, char *argv[]) {
r = parse_argv(argc, argv);
if (r <= 0)
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ goto finish;
+
+ if (arg_commit)
+ r = machine_id_commit(arg_root);
+ else
+ r = machine_id_setup(arg_root, SD_ID128_NULL);
- return machine_id_setup(arg_root) < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+finish:
+ free(arg_root);
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c
index 2453a9ff04..4ec1766033 100644
--- a/src/machine/image-dbus.c
+++ b/src/machine/image-dbus.c
@@ -19,11 +19,13 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "alloc-util.h"
#include "bus-label.h"
-#include "strv.h"
#include "bus-util.h"
-#include "machine-image.h"
#include "image-dbus.h"
+#include "machine-image.h"
+#include "strv.h"
+#include "user-util.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, image_type, ImageType);
diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c
index b010c90989..03d32c6ed7 100644
--- a/src/machine/machine-dbus.c
+++ b/src/machine/machine-dbus.c
@@ -29,23 +29,27 @@
#include <libgen.h>
#undef basename
-#include "bus-util.h"
-#include "bus-label.h"
-#include "strv.h"
+#include "alloc-util.h"
#include "bus-common-errors.h"
+#include "bus-internal.h"
+#include "bus-label.h"
+#include "bus-util.h"
#include "copy.h"
+#include "env-util.h"
+#include "fd-util.h"
#include "fileio.h"
+#include "formats-util.h"
+#include "fs-util.h"
#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 "machine.h"
+#include "mkdir.h"
+#include "path-util.h"
#include "process-util.h"
-#include "env-util.h"
+#include "strv.h"
#include "terminal-util.h"
+#include "user-util.h"
static int property_get_id(
sd_bus *bus,
@@ -188,7 +192,7 @@ int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro
}
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_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
Machine *m = userdata;
int r;
@@ -371,7 +375,7 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd
}
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_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_strv_free_ char **l = NULL;
Machine *m = userdata;
char **k, **v;
@@ -477,7 +481,7 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s
}
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_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_free_ char *pty_name = NULL;
_cleanup_close_ int master = -1;
Machine *m = userdata;
@@ -532,7 +536,7 @@ static int container_bus_new(Machine *m, sd_bus_error *error, sd_bus **ret) {
break;
case MACHINE_CONTAINER: {
- _cleanup_bus_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
char *address;
r = sd_bus_new(&bus);
@@ -566,9 +570,9 @@ static int container_bus_new(Machine *m, sd_bus_error *error, sd_bus **ret) {
}
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_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_free_ char *pty_name = NULL;
- _cleanup_bus_flush_close_unref_ sd_bus *allocated_bus = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *allocated_bus = NULL;
_cleanup_close_ int master = -1;
sd_bus *container_bus = NULL;
Machine *m = userdata;
@@ -635,11 +639,11 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu
}
int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *tm = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *tm = NULL;
_cleanup_free_ char *pty_name = NULL;
- _cleanup_bus_flush_close_unref_ sd_bus *allocated_bus = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *allocated_bus = NULL;
sd_bus *container_bus = NULL;
- _cleanup_close_ int master = -1;
+ _cleanup_close_ int master = -1, slave = -1;
_cleanup_strv_free_ char **env = NULL, **args = NULL;
Machine *m = userdata;
const char *p, *unit, *user, *path, *description, *utmp_id;
@@ -700,8 +704,11 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
return r;
p = path_startswith(pty_name, "/dev/pts/");
- if (!p)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "PTS name %s is invalid", pty_name);
+ assert(p);
+
+ slave = machine_open_terminal(m, pty_name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (slave < 0)
+ return slave;
utmp_id = path_startswith(pty_name, "/dev/");
assert(utmp_id);
@@ -735,20 +742,19 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
description = strjoina("Shell for User ", isempty(user) ? "root" : user);
r = sd_bus_message_append(tm,
- "(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)",
+ "(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)",
"Description", "s", description,
- "StandardInput", "s", "tty",
- "StandardOutput", "s", "tty",
- "StandardError", "s", "tty",
- "TTYPath", "s", pty_name,
+ "StandardInputFileDescriptor", "h", slave,
+ "StandardOutputFileDescriptor", "h", slave,
+ "StandardErrorFileDescriptor", "h", slave,
"SendSIGHUP", "b", true,
"IgnoreSIGPIPE", "b", false,
"KillMode", "s", "mixed",
- "TTYVHangup", "b", true,
"TTYReset", "b", true,
"UtmpIdentifier", "s", utmp_id,
"UtmpMode", "s", "user",
- "PAMName", "s", "login");
+ "PAMName", "s", "login",
+ "WorkingDirectory", "s", "-~");
if (r < 0)
return r;
@@ -844,6 +850,8 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
if (r < 0)
return r;
+ slave = safe_close(slave);
+
r = sd_bus_message_new_method_return(message, &reply);
if (r < 0)
return r;
@@ -914,7 +922,7 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu
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
+ MS_MOVE cannot be applied to mounts with MS_SHARED parent
mounts.) */
if (!mkdtemp(mount_slave))
@@ -1078,7 +1086,7 @@ finish:
}
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;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
MachineOperation *o = userdata;
int r;
@@ -1304,7 +1312,7 @@ int machine_object_find(sd_bus *bus, const char *path, const char *interface, vo
assert(m);
if (streq(path, "/org/freedesktop/machine1/machine/self")) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
sd_bus_message *message;
pid_t pid;
@@ -1403,7 +1411,7 @@ int machine_send_signal(Machine *m, bool new_machine) {
}
int machine_send_create_reply(Machine *m, sd_bus_error *error) {
- _cleanup_bus_message_unref_ sd_bus_message *c = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL;
_cleanup_free_ char *p = NULL;
assert(m);
diff --git a/src/machine/machine-dbus.h b/src/machine/machine-dbus.h
index 38b46ad936..194e680e05 100644
--- a/src/machine/machine-dbus.h
+++ b/src/machine/machine-dbus.h
@@ -23,6 +23,8 @@
#include "sd-bus.h"
+#include "machine.h"
+
extern const sd_bus_vtable machine_vtable[];
char *machine_bus_path(Machine *s);
diff --git a/src/machine/machine.c b/src/machine/machine.c
index b52ecd015c..3fe6f8b072 100644
--- a/src/machine/machine.c
+++ b/src/machine/machine.c
@@ -19,23 +19,31 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
#include <string.h>
#include <unistd.h>
-#include <errno.h>
#include "sd-messages.h"
-#include "util.h"
-#include "mkdir.h"
-#include "hashmap.h"
+#include "alloc-util.h"
+#include "bus-error.h"
+#include "bus-util.h"
+#include "escape.h"
+#include "extract-word.h"
+#include "fd-util.h"
#include "fileio.h"
+#include "formats-util.h"
+#include "hashmap.h"
+#include "machine-dbus.h"
+#include "machine.h"
+#include "mkdir.h"
+#include "parse-util.h"
+#include "process-util.h"
#include "special.h"
+#include "string-table.h"
+#include "terminal-util.h"
#include "unit-name.h"
-#include "bus-util.h"
-#include "bus-error.h"
-#include "machine.h"
-#include "machine-dbus.h"
-#include "formats-util.h"
+#include "util.h"
Machine* machine_new(Manager *manager, MachineClass class, const char *name) {
Machine *m;
@@ -98,7 +106,7 @@ void machine_free(Machine *m) {
m->manager->host_machine = NULL;
if (m->leader > 0)
- (void) hashmap_remove_value(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
+ (void) hashmap_remove_value(m->manager->machine_leaders, PID_TO_PTR(m->leader), m);
sd_bus_message_unref(m->create_message);
@@ -306,19 +314,26 @@ int machine_load(Machine *m) {
}
if (netif) {
- size_t l, allocated = 0, nr = 0;
- const char *word, *state;
+ size_t allocated = 0, nr = 0;
+ const char *p;
int *ni = NULL;
- FOREACH_WORD(word, l, netif, state) {
- char buf[l+1];
+ p = netif;
+ for(;;) {
+ _cleanup_free_ char *word = NULL;
int ifi;
- *(char*) (mempcpy(buf, word, l)) = 0;
+ r = extract_first_word(&p, &word, NULL, 0);
+ if (r == 0)
+ break;
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_warning_errno(r, "Failed to parse NETIF: %s", netif);
+ break;
+ }
- if (safe_atoi(buf, &ifi) < 0)
- continue;
- if (ifi <= 0)
+ if (parse_ifindex(word, &ifi) < 0)
continue;
if (!GREEDY_REALLOC(ni, allocated, nr+1)) {
@@ -387,7 +402,7 @@ int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
if (m->started)
return 0;
- r = hashmap_put(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
+ r = hashmap_put(m->manager->machine_leaders, PID_TO_PTR(m->leader), m);
if (r < 0)
return r;
@@ -417,7 +432,7 @@ int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
}
static int machine_stop_scope(Machine *m) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
char *job = NULL;
int r;
@@ -538,7 +553,7 @@ int machine_kill(Machine *m, KillWho who, int signo) {
return 0;
}
- /* Otherwise make PID 1 do it for us, for the entire cgroup */
+ /* Otherwise, make PID 1 do it for us, for the entire cgroup */
return manager_kill_unit(m->manager, m->unit, signo, NULL);
}
@@ -571,6 +586,25 @@ int machine_openpt(Machine *m, int flags) {
}
}
+int machine_open_terminal(Machine *m, const char *path, int mode) {
+ assert(m);
+
+ switch (m->class) {
+
+ case MACHINE_HOST:
+ return open_terminal(path, mode);
+
+ case MACHINE_CONTAINER:
+ if (m->leader <= 0)
+ return -EINVAL;
+
+ return open_terminal_in_namespace(m->leader, path, mode);
+
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
MachineOperation *machine_operation_unref(MachineOperation *o) {
if (!o)
return NULL;
diff --git a/src/machine/machine.h b/src/machine/machine.h
index 5f978289f2..ad7f2a162f 100644
--- a/src/machine/machine.h
+++ b/src/machine/machine.h
@@ -123,3 +123,4 @@ const char *kill_who_to_string(KillWho k) _const_;
KillWho kill_who_from_string(const char *s) _pure_;
int machine_openpt(Machine *m, int flags);
+int machine_open_terminal(Machine *m, const char *path, int mode);
diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c
index d276fbe956..fd454310ae 100644
--- a/src/machine/machinectl.c
+++ b/src/machine/machinectl.c
@@ -19,44 +19,47 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/socket.h>
-#include <unistd.h>
+#include <arpa/inet.h>
#include <errno.h>
-#include <string.h>
+#include <fcntl.h>
#include <getopt.h>
#include <locale.h>
-#include <fcntl.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
#include <net/if.h>
+#include <netinet/in.h>
+#include <string.h>
#include <sys/mount.h>
+#include <sys/socket.h>
+#include <unistd.h>
#include "sd-bus.h"
-#include "log.h"
-#include "util.h"
-#include "macro.h"
-#include "pager.h"
-#include "spawn-polkit-agent.h"
-#include "bus-util.h"
+
+#include "alloc-util.h"
#include "bus-error.h"
-#include "build.h"
-#include "strv.h"
-#include "unit-name.h"
+#include "bus-util.h"
#include "cgroup-show.h"
-#include "logs-show.h"
#include "cgroup-util.h"
-#include "ptyfwd.h"
-#include "event-util.h"
-#include "path-util.h"
-#include "mkdir.h"
#include "copy.h"
-#include "verbs.h"
+#include "env-util.h"
+#include "fd-util.h"
+#include "hostname-util.h"
#include "import-util.h"
+#include "log.h"
+#include "logs-show.h"
+#include "macro.h"
+#include "mkdir.h"
+#include "pager.h"
+#include "parse-util.h"
+#include "path-util.h"
#include "process-util.h"
-#include "terminal-util.h"
+#include "ptyfwd.h"
#include "signal-util.h"
-#include "env-util.h"
-#include "hostname-util.h"
+#include "spawn-polkit-agent.h"
+#include "strv.h"
+#include "terminal-util.h"
+#include "unit-name.h"
+#include "util.h"
+#include "verbs.h"
+#include "web-util.h"
static char **arg_property = NULL;
static bool arg_all = false;
@@ -75,7 +78,6 @@ static unsigned arg_lines = 10;
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 const char *arg_uid = NULL;
static char **arg_setenv = NULL;
@@ -106,7 +108,7 @@ static OutputFlags get_output_flags(void) {
arg_all * OUTPUT_SHOW_ALL |
arg_full * OUTPUT_FULL_WIDTH |
(!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
- on_tty() * OUTPUT_COLOR |
+ colors_enabled() * OUTPUT_COLOR |
!arg_quiet * OUTPUT_WARN_CUTOFF;
}
@@ -125,8 +127,8 @@ static int compare_machine_info(const void *a, const void *b) {
static int list_machines(int argc, char *argv[], void *userdata) {
size_t max_name = strlen("MACHINE"), max_class = strlen("CLASS"), max_service = strlen("SERVICE");
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_free_ MachineInfo *machines = NULL;
const char *name, *class, *service, *object;
size_t n_machines = 0, n_allocated = 0, j;
@@ -226,9 +228,9 @@ static int compare_image_info(const void *a, const void *b) {
static int list_images(int argc, char *argv[], void *userdata) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
size_t max_name = strlen("NAME"), max_type = strlen("TYPE"), max_size = strlen("USAGE"), max_crtime = strlen("CREATED"), max_mtime = strlen("MODIFIED");
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_free_ ImageInfo *images = NULL;
size_t n_images = 0, n_allocated = 0, j;
const char *name, *type, *object;
@@ -340,8 +342,8 @@ static int list_images(int argc, char *argv[], void *userdata) {
}
static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_free_ char *path = NULL;
const char *cgroup;
int r;
@@ -389,7 +391,7 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
}
static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
int r;
assert(bus);
@@ -450,7 +452,7 @@ static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *p
}
static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
const char *k, *v, *pretty = NULL;
int r;
@@ -697,8 +699,8 @@ static int show_machine_properties(sd_bus *bus, const char *path, bool *new_line
static int show_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 *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
bool properties, new_line = false;
sd_bus *bus = userdata;
int r = 0, i;
@@ -946,8 +948,8 @@ static int show_image_properties(sd_bus *bus, const char *path, bool *new_line)
static int show_image(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;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
bool properties, new_line = false;
sd_bus *bus = userdata;
int r = 0, i;
@@ -1002,7 +1004,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;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
int r, i;
@@ -1047,7 +1049,7 @@ 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;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
int r, i;
@@ -1075,7 +1077,7 @@ static int terminate_machine(int argc, char *argv[], void *userdata) {
}
static int copy_files(int argc, char *argv[], void *userdata) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_free_ char *abs_host_path = NULL;
char *dest, *host_path, *container_path;
sd_bus *bus = userdata;
@@ -1092,9 +1094,10 @@ static int copy_files(int argc, char *argv[], void *userdata) {
container_path = copy_from ? argv[2] : dest;
if (!path_is_absolute(host_path)) {
- abs_host_path = path_make_absolute_cwd(host_path);
- if (!abs_host_path)
- return log_oom();
+ r = path_make_absolute_cwd(host_path, &abs_host_path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to make path absolute: %m");
+
host_path = abs_host_path;
}
@@ -1110,16 +1113,14 @@ static int copy_files(int argc, char *argv[], void *userdata) {
argv[1],
copy_from ? container_path : host_path,
copy_from ? host_path : container_path);
- if (r < 0) {
- log_error("Failed to copy: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to copy: %s", bus_error_message(&error, r));
return 0;
}
static int bind_mount(int argc, char *argv[], void *userdata) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
int r;
@@ -1173,7 +1174,7 @@ static int on_machine_removed(sd_bus_message *m, void *userdata, sd_bus_error *r
return 0;
}
-static int process_forward(sd_event *event, PTYForward **forward, int master, bool ignore_vhangup, const char *name) {
+static int process_forward(sd_event *event, PTYForward **forward, int master, PTYForwardFlags flags, const char *name) {
char last_char = 0;
bool machine_died;
int ret = 0, r;
@@ -1192,7 +1193,7 @@ static int process_forward(sd_event *event, PTYForward **forward, int master, bo
sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
- r = pty_forward_new(event, master, ignore_vhangup, false, forward);
+ r = pty_forward_new(event, master, flags, forward);
if (r < 0)
return log_error_errno(r, "Failed to create PTY forwarder: %m");
@@ -1203,7 +1204,7 @@ static int process_forward(sd_event *event, PTYForward **forward, int master, bo
pty_forward_get_last_char(*forward, &last_char);
machine_died =
- ignore_vhangup &&
+ (flags & PTY_FORWARD_IGNORE_VHANGUP) &&
pty_forward_get_ignore_vhangup(*forward) == 0;
*forward = pty_forward_free(*forward);
@@ -1223,11 +1224,11 @@ static int process_forward(sd_event *event, PTYForward **forward, int master, bo
}
static int login_machine(int argc, char *argv[], void *userdata) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(pty_forward_freep) PTYForward *forward = NULL;
- _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
- _cleanup_event_unref_ sd_event *event = NULL;
+ _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
int master = -1, r;
sd_bus *bus = userdata;
const char *pty, *match, *machine;
@@ -1286,15 +1287,15 @@ static int login_machine(int argc, char *argv[], void *userdata) {
if (r < 0)
return bus_log_parse_error(r);
- return process_forward(event, &forward, master, true, machine);
+ return process_forward(event, &forward, master, PTY_FORWARD_IGNORE_VHANGUP, machine);
}
static int shell_machine(int argc, char *argv[], void *userdata) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *m = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(pty_forward_freep) PTYForward *forward = NULL;
- _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
- _cleanup_event_unref_ sd_event *event = NULL;
+ _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
int master = -1, r;
sd_bus *bus = userdata;
const char *pty, *match, *machine, *path, *uid = NULL;
@@ -1390,11 +1391,11 @@ static int shell_machine(int argc, char *argv[], void *userdata) {
if (r < 0)
return bus_log_parse_error(r);
- return process_forward(event, &forward, master, false, machine);
+ return process_forward(event, &forward, master, 0, machine);
}
static int remove_image(int argc, char *argv[], void *userdata) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
int r, i;
@@ -1422,7 +1423,7 @@ static int remove_image(int argc, char *argv[], void *userdata) {
}
static int rename_image(int argc, char *argv[], void *userdata) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
int r;
@@ -1446,7 +1447,7 @@ static int rename_image(int argc, char *argv[], void *userdata) {
}
static int clone_image(int argc, char *argv[], void *userdata) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
int r;
@@ -1470,7 +1471,7 @@ static int clone_image(int argc, char *argv[], void *userdata) {
}
static int read_only_image(int argc, char *argv[], void *userdata) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
int b = true, r;
@@ -1525,7 +1526,7 @@ static int make_service_name(const char *name, char **ret) {
}
static int start_machine(int argc, char *argv[], void *userdata) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
sd_bus *bus = userdata;
int r, i;
@@ -1539,7 +1540,7 @@ 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 *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_free_ char *unit = NULL;
const char *object;
@@ -1570,7 +1571,7 @@ static int start_machine(int argc, char *argv[], void *userdata) {
return log_oom();
}
- r = bus_wait_for_jobs(w, arg_quiet);
+ r = bus_wait_for_jobs(w, arg_quiet, NULL);
if (r < 0)
return r;
@@ -1578,8 +1579,8 @@ static int start_machine(int argc, char *argv[], void *userdata) {
}
static int enable_machine(int argc, char *argv[], void *userdata) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int carries_install_info = 0;
const char *method = NULL;
sd_bus *bus = userdata;
@@ -1718,10 +1719,10 @@ static int transfer_signal_handler(sd_event_source *s, const struct signalfd_sig
}
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;
- _cleanup_event_unref_ sd_event* event = NULL;
+ _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot_job_removed = NULL, *slot_log_message = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(sd_event_unrefp) sd_event* event = NULL;
const char *path = NULL;
uint32_t id;
int r;
@@ -1788,7 +1789,7 @@ static int transfer_image_common(sd_bus *bus, sd_bus_message *m) {
}
static int import_tar(int argc, char *argv[], void *userdata) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *ll = NULL;
_cleanup_close_ int fd = -1;
const char *local = NULL, *path = NULL;
@@ -1855,7 +1856,7 @@ static int import_tar(int argc, char *argv[], void *userdata) {
}
static int import_raw(int argc, char *argv[], void *userdata) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *ll = NULL;
_cleanup_close_ int fd = -1;
const char *local = NULL, *path = NULL;
@@ -1937,7 +1938,7 @@ static void determine_compression_from_filename(const char *p) {
}
static int export_tar(int argc, char *argv[], void *userdata) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_close_ int fd = -1;
const char *local = NULL, *path = NULL;
sd_bus *bus = userdata;
@@ -1987,7 +1988,7 @@ static int export_tar(int argc, char *argv[], void *userdata) {
}
static int export_raw(int argc, char *argv[], void *userdata) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_close_ int fd = -1;
const char *local = NULL, *path = NULL;
sd_bus *bus = userdata;
@@ -2037,7 +2038,7 @@ static int export_raw(int argc, char *argv[], void *userdata) {
}
static int pull_tar(int argc, char *argv[], void *userdata) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *l = NULL, *ll = NULL;
const char *local, *remote;
sd_bus *bus = userdata;
@@ -2101,7 +2102,7 @@ static int pull_tar(int argc, char *argv[], void *userdata) {
}
static int pull_raw(int argc, char *argv[], void *userdata) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *l = NULL, *ll = NULL;
const char *local, *remote;
sd_bus *bus = userdata;
@@ -2164,78 +2165,6 @@ static int pull_raw(int argc, char *argv[], void *userdata) {
return transfer_image_common(bus, m);
}
-static int pull_dkr(int argc, char *argv[], void *userdata) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- const char *local, *remote, *tag;
- sd_bus *bus = userdata;
- int r;
-
- if (arg_verify != IMPORT_VERIFY_NO) {
- log_error("Imports from DKR do not support image verification, please pass --verify=no.");
- return -EINVAL;
- }
-
- remote = argv[1];
- tag = strchr(remote, ':');
- if (tag) {
- remote = strndupa(remote, tag - remote);
- tag++;
- }
-
- if (!dkr_name_is_valid(remote)) {
- log_error("DKR name '%s' is invalid.", remote);
- return -EINVAL;
- }
- if (tag && !dkr_tag_is_valid(tag)) {
- log_error("DKR tag '%s' is invalid.", remote);
- return -EINVAL;
- }
-
- if (argc >= 3)
- local = argv[2];
- else {
- local = strchr(remote, '/');
- if (local)
- local++;
- else
- local = remote;
- }
-
- if (isempty(local) || streq(local, "-"))
- local = NULL;
-
- if (local) {
- if (!machine_name_is_valid(local)) {
- log_error("Local name %s is not a suitable machine name.", local);
- return -EINVAL;
- }
- }
-
- r = sd_bus_message_new_method_call(
- bus,
- &m,
- "org.freedesktop.import1",
- "/org/freedesktop/import1",
- "org.freedesktop.import1.Manager",
- "PullDkr");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append(
- m,
- "sssssb",
- arg_dkr_index_url,
- remote,
- tag,
- local,
- import_verify_to_string(arg_verify),
- arg_force);
- if (r < 0)
- return bus_log_create_error(r);
-
- return transfer_image_common(bus, m);
-}
-
typedef struct TransferInfo {
uint32_t id;
const char *type;
@@ -2252,8 +2181,8 @@ static int compare_transfer_info(const void *a, const void *b) {
static int list_transfers(int argc, char *argv[], void *userdata) {
size_t max_type = strlen("TYPE"), max_local = strlen("LOCAL"), max_remote = strlen("REMOTE");
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_free_ TransferInfo *transfers = NULL;
size_t n_transfers = 0, n_allocated = 0, j;
const char *type, *remote, *local, *object;
@@ -2343,7 +2272,7 @@ static int list_transfers(int argc, char *argv[], void *userdata) {
}
static int cancel_transfer(int argc, char *argv[], void *userdata) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
int r, i;
@@ -2377,12 +2306,12 @@ static int cancel_transfer(int argc, char *argv[], void *userdata) {
}
static int set_limit(int argc, char *argv[], void *userdata) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_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], "-"))
+ if (STR_IN_SET(argv[argc-1], "-", "none", "infinity"))
limit = (uint64_t) -1;
else {
r = parse_size(argv[argc-1], 1024, &limit);
@@ -2450,9 +2379,7 @@ static int help(int argc, char *argv[], void *userdata) {
" json-pretty, json-sse, cat)\n"
" --verify=MODE Verification mode for downloaded images (no,\n"
" checksum, signature)\n"
- " --force Download image even if already exists\n"
- " --dkr-index-url=URL Specify the index URL to use for DKR image\n"
- " downloads\n\n"
+ " --force Download image even if already exists\n\n"
"Machine Commands:\n"
" list List running VMs and containers\n"
" status NAME... Show VM/container details\n"
@@ -2484,7 +2411,6 @@ static int help(int argc, char *argv[], void *userdata) {
"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"
@@ -2508,7 +2434,6 @@ static int parse_argv(int argc, char *argv[]) {
ARG_NO_ASK_PASSWORD,
ARG_VERIFY,
ARG_FORCE,
- ARG_DKR_INDEX_URL,
ARG_FORMAT,
ARG_UID,
ARG_SETENV,
@@ -2534,7 +2459,6 @@ static int parse_argv(int argc, char *argv[]) {
{ "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
{ "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 },
{ "uid", required_argument, NULL, ARG_UID },
{ "setenv", required_argument, NULL, ARG_SETENV },
@@ -2554,9 +2478,7 @@ static int parse_argv(int argc, char *argv[]) {
return help(0, NULL, NULL);
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case 'p':
r = strv_extend(&arg_property, optarg);
@@ -2650,15 +2572,6 @@ static int parse_argv(int argc, char *argv[]) {
arg_force = true;
break;
- case ARG_DKR_INDEX_URL:
- if (!http_url_is_valid(optarg)) {
- log_error("Index URL is invalid: %s", optarg);
- return -EINVAL;
- }
-
- arg_dkr_index_url = optarg;
- break;
-
case ARG_FORMAT:
if (!STR_IN_SET(optarg, "uncompressed", "xz", "gzip", "bzip2")) {
log_error("Unknown format: %s", optarg);
@@ -2725,7 +2638,6 @@ static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
{ "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 },
@@ -2736,7 +2648,7 @@ static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
}
int main(int argc, char*argv[]) {
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
setlocale(LC_ALL, "");
@@ -2747,7 +2659,7 @@ int main(int argc, char*argv[]) {
if (r <= 0)
goto finish;
- r = bus_open_transport(arg_transport, arg_host, false, &bus);
+ r = bus_connect_transport(arg_transport, arg_host, false, &bus);
if (r < 0) {
log_error_errno(r, "Failed to create bus connection: %m");
goto finish;
diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c
index 41bb106d28..e448dd2035 100644
--- a/src/machine/machined-dbus.c
+++ b/src/machine/machined-dbus.c
@@ -24,21 +24,26 @@
#include <unistd.h>
#include "sd-id128.h"
-#include "strv.h"
-#include "path-util.h"
-#include "unit-name.h"
-#include "bus-util.h"
+
+#include "alloc-util.h"
+#include "btrfs-util.h"
#include "bus-common-errors.h"
+#include "bus-util.h"
#include "cgroup-util.h"
-#include "btrfs-util.h"
+#include "fd-util.h"
#include "formats-util.h"
-#include "process-util.h"
#include "hostname-util.h"
+#include "image-dbus.h"
+#include "machine-dbus.h"
#include "machine-image.h"
#include "machine-pool.h"
-#include "image-dbus.h"
#include "machined.h"
-#include "machine-dbus.h"
+#include "path-util.h"
+#include "process-util.h"
+#include "stdio-util.h"
+#include "strv.h"
+#include "unit-name.h"
+#include "user-util.h"
static int property_get_pool_path(
sd_bus *bus,
@@ -79,7 +84,7 @@ static int property_get_pool_usage(
if (fd >= 0) {
BtrfsQuotaInfo q;
- if (btrfs_subvol_get_quota_fd(fd, &q) >= 0)
+ if (btrfs_subvol_get_subtree_quota_fd(fd, 0, &q) >= 0)
usage = q.referenced;
}
@@ -115,7 +120,7 @@ static int property_get_pool_limit(
if (fd >= 0) {
BtrfsQuotaInfo q;
- if (btrfs_subvol_get_quota_fd(fd, &q) >= 0)
+ if (btrfs_subvol_get_subtree_quota_fd(fd, 0, &q) >= 0)
size = q.referenced_max;
}
@@ -194,8 +199,11 @@ static int method_get_machine_by_pid(sd_bus_message *message, void *userdata, sd
if (r < 0)
return r;
+ if (pid < 0)
+ return -EINVAL;
+
if (pid == 0) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
if (r < 0)
@@ -220,7 +228,7 @@ static int method_get_machine_by_pid(sd_bus_message *message, void *userdata, sd
}
static int method_list_machines(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
Manager *m = userdata;
Machine *machine;
Iterator i;
@@ -325,7 +333,7 @@ static int method_create_or_register_machine(Manager *manager, sd_bus_message *m
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Root directory must be empty or an absolute path");
if (leader == 0) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
if (r < 0)
@@ -546,7 +554,7 @@ static int method_get_machine_os_release(sd_bus_message *message, void *userdata
}
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_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(image_hashmap_freep) Hashmap *images = NULL;
Manager *m = userdata;
Image *image;
@@ -831,7 +839,9 @@ static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus
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);
+ (void) btrfs_qgroup_set_limit("/var/lib/machines", 0, limit);
+
+ r = btrfs_subvol_set_subtree_quota_limit("/var/lib/machines", 0, limit);
if (r == -ENOTTY)
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Quota is only supported on btrfs.");
if (r < 0)
@@ -900,7 +910,7 @@ static int method_map_from_machine_user(sd_bus_message *message, void *userdata,
if (k < 0 && feof(f))
break;
if (k != 3) {
- if (ferror(f) && errno != 0)
+ if (ferror(f) && errno > 0)
return -errno;
return -EIO;
@@ -958,7 +968,7 @@ static int method_map_to_machine_user(sd_bus_message *message, void *userdata, s
if (k < 0 && feof(f))
break;
if (k != 3) {
- if (ferror(f) && errno != 0)
+ if (ferror(f) && errno > 0)
return -errno;
return -EIO;
@@ -1018,7 +1028,7 @@ static int method_map_from_machine_group(sd_bus_message *message, void *groupdat
if (k < 0 && feof(f))
break;
if (k != 3) {
- if (ferror(f) && errno != 0)
+ if (ferror(f) && errno > 0)
return -errno;
return -EIO;
@@ -1076,7 +1086,7 @@ static int method_map_to_machine_group(sd_bus_message *message, void *groupdata,
if (k < 0 && feof(f))
break;
if (k != 3) {
- if (ferror(f) && errno != 0)
+ if (ferror(f) && errno > 0)
return -errno;
return -EIO;
@@ -1166,7 +1176,7 @@ int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *err
if (streq(result, "done"))
machine_send_create_reply(machine, NULL);
else {
- _cleanup_bus_error_free_ sd_bus_error e = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL;
sd_bus_error_setf(&e, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result);
@@ -1270,7 +1280,7 @@ int manager_start_scope(
sd_bus_error *error,
char **job) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
int r;
assert(manager);
@@ -1315,6 +1325,10 @@ int manager_start_scope(
if (r < 0)
return r;
+ r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", 8192);
+ if (r < 0)
+ return bus_log_create_error(r);
+
if (more_properties) {
r = sd_bus_message_copy(m, more_properties, true);
if (r < 0)
@@ -1352,7 +1366,7 @@ int manager_start_scope(
}
int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
int r;
assert(manager);
@@ -1415,8 +1429,8 @@ int manager_kill_unit(Manager *manager, const char *unit, int signo, sd_bus_erro
}
int manager_unit_is_active(Manager *manager, const char *unit) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_free_ char *path = NULL;
const char *state;
int r;
@@ -1457,8 +1471,8 @@ int manager_unit_is_active(Manager *manager, const char *unit) {
}
int manager_job_is_active(Manager *manager, const char *path) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
int r;
assert(manager);
@@ -1498,7 +1512,7 @@ int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine) {
assert(pid >= 1);
assert(machine);
- mm = hashmap_get(m->machine_leaders, UINT_TO_PTR(pid));
+ mm = hashmap_get(m->machine_leaders, PID_TO_PTR(pid));
if (!mm) {
_cleanup_free_ char *unit = NULL;
diff --git a/src/machine/machined.c b/src/machine/machined.c
index df3cc9972a..df6318ca20 100644
--- a/src/machine/machined.c
+++ b/src/machine/machined.c
@@ -24,15 +24,19 @@
#include <unistd.h>
#include "sd-daemon.h"
-#include "cgroup-util.h"
-#include "bus-util.h"
+
+#include "alloc-util.h"
#include "bus-error.h"
-#include "label.h"
+#include "bus-util.h"
+#include "cgroup-util.h"
+#include "dirent-util.h"
+#include "fd-util.h"
#include "formats-util.h"
-#include "signal-util.h"
#include "hostname-util.h"
+#include "label.h"
#include "machine-image.h"
#include "machined.h"
+#include "signal-util.h"
Manager *manager_new(void) {
Manager *m;
@@ -146,8 +150,7 @@ int manager_enumerate_machines(Manager *m) {
if (errno == ENOENT)
return 0;
- log_error_errno(errno, "Failed to open /run/systemd/machines: %m");
- return -errno;
+ return log_error_errno(errno, "Failed to open /run/systemd/machines: %m");
}
FOREACH_DIRENT(de, d, return -errno) {
@@ -181,7 +184,7 @@ int manager_enumerate_machines(Manager *m) {
}
static int manager_connect_bus(Manager *m) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert(m);
diff --git a/src/machine/machined.h b/src/machine/machined.h
index b3e59bf998..bc5d4abb80 100644
--- a/src/machine/machined.h
+++ b/src/machine/machined.h
@@ -23,16 +23,17 @@
#include <stdbool.h>
-#include "list.h"
-#include "hashmap.h"
-#include "sd-event.h"
#include "sd-bus.h"
+#include "sd-event.h"
+
+#include "hashmap.h"
+#include "list.h"
typedef struct Manager Manager;
-#include "machine.h"
-#include "machine-dbus.h"
#include "image-dbus.h"
+#include "machine-dbus.h"
+#include "machine.h"
struct Manager {
sd_event *event;
diff --git a/src/machine/test-machine-tables.c b/src/machine/test-machine-tables.c
index 4aae426050..f851a4d37d 100644
--- a/src/machine/test-machine-tables.c
+++ b/src/machine/test-machine-tables.c
@@ -18,7 +18,6 @@
***/
#include "machine.h"
-
#include "test-tables.h"
int main(int argc, char **argv) {
diff --git a/src/modules-load/modules-load.c b/src/modules-load/modules-load.c
index 5bbe314ba0..a7fdcb09cf 100644
--- a/src/modules-load/modules-load.c
+++ b/src/modules-load/modules-load.c
@@ -20,21 +20,25 @@
***/
#include <errno.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <limits.h>
#include <getopt.h>
#include <libkmod.h>
+#include <limits.h>
+#include <string.h>
+#include <sys/stat.h>
+#include "conf-files.h"
+#include "def.h"
+#include "fd-util.h"
+#include "fileio.h"
#include "log.h"
-#include "util.h"
+#include "proc-cmdline.h"
+#include "string-util.h"
#include "strv.h"
-#include "conf-files.h"
-#include "build.h"
+#include "util.h"
static char **arg_proc_cmdline_modules = NULL;
-static const char conf_file_dirs[] = CONF_DIRS_NULSTR("modules-load");
+static const char conf_file_dirs[] = CONF_PATHS_NULSTR("modules-load.d");
static void systemd_kmod_log(void *data, int priority, const char *file, int line,
const char *fn, const char *format, va_list args) {
@@ -51,7 +55,7 @@ static int add_modules(const char *p) {
if (!k)
return log_oom();
- if (strv_extend_strv(&arg_proc_cmdline_modules, k) < 0)
+ if (strv_extend_strv(&arg_proc_cmdline_modules, k, true) < 0)
return log_oom();
return 0;
@@ -147,8 +151,7 @@ static int apply_file(struct kmod_ctx *ctx, const char *path, bool ignore_enoent
if (feof(f))
break;
- log_error_errno(errno, "Failed to read file '%s', ignoring: %m", path);
- return -errno;
+ return log_error_errno(errno, "Failed to read file '%s', ignoring: %m", path);
}
l = strstrip(line);
@@ -199,9 +202,7 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case '?':
return -EINVAL;
diff --git a/src/network/networkctl.c b/src/network/networkctl.c
index 75572b6388..4a8fa4d8f3 100644
--- a/src/network/networkctl.c
+++ b/src/network/networkctl.c
@@ -19,29 +19,34 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdbool.h>
#include <getopt.h>
#include <net/if.h>
+#include <stdbool.h>
-#include "sd-network.h"
-#include "sd-netlink.h"
-#include "sd-hwdb.h"
#include "sd-device.h"
+#include "sd-hwdb.h"
+#include "sd-netlink.h"
+#include "sd-network.h"
-#include "strv.h"
-#include "build.h"
-#include "util.h"
-#include "pager.h"
-#include "lldp.h"
-#include "netlink-util.h"
+#include "alloc-util.h"
+#include "arphrd-list.h"
#include "device-util.h"
+#include "ether-addr-util.h"
#include "hwdb-util.h"
-#include "arphrd-list.h"
+#include "lldp.h"
#include "local-addresses.h"
+#include "locale-util.h"
+#include "netlink-util.h"
+#include "pager.h"
+#include "parse-util.h"
#include "socket-util.h"
-#include "ether-addr-util.h"
-#include "verbs.h"
+#include "stdio-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "strv.h"
#include "terminal-util.h"
+#include "util.h"
+#include "verbs.h"
static bool arg_no_pager = false;
static bool arg_legend = true;
@@ -192,8 +197,8 @@ 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_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
- _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
_cleanup_free_ LinkInfo *links = NULL;
int r, c, i;
@@ -224,7 +229,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_device_unref_ sd_device *d = NULL;
+ _cleanup_(sd_device_unrefp) 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)];
@@ -271,7 +276,8 @@ static int ieee_oui(sd_hwdb *hwdb, struct ether_addr *mac, char **ret) {
if (memcmp(mac, "\0\0\0", 3) == 0)
return -EINVAL;
- snprintf(modalias, sizeof(modalias), "OUI:" ETHER_ADDR_FORMAT_STR, ETHER_ADDR_FORMAT_VAL(*mac));
+ xsprintf(modalias, "OUI:" ETHER_ADDR_FORMAT_STR,
+ ETHER_ADDR_FORMAT_VAL(*mac));
r = sd_hwdb_get(hwdb, modalias, "ID_OUI_FROM_DATABASE", &description);
if (r < 0)
@@ -293,7 +299,7 @@ static int get_gateway_description(
int family,
union in_addr_union *gateway,
char **gateway_description) {
- _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
sd_netlink_message *m;
int r;
@@ -498,8 +504,8 @@ static int link_status_one(
const char *name) {
_cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
_cleanup_free_ char *setup_state = NULL, *operational_state = NULL, *tz = NULL;
- _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
- _cleanup_device_unref_ sd_device *d = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
+ _cleanup_(sd_device_unrefp) 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;
@@ -516,7 +522,7 @@ static int link_status_one(
assert(rtnl);
assert(name);
- if (safe_atoi(name, &ifindex) >= 0 && ifindex > 0)
+ if (parse_ifindex(name, &ifindex) >= 0)
r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
else {
r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
@@ -670,8 +676,8 @@ static int link_status_one(
}
static int link_status(int argc, char *argv[], void *userdata) {
- _cleanup_hwdb_unref_ sd_hwdb *hwdb = NULL;
- _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
+ _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
char **name;
int r;
@@ -716,7 +722,7 @@ static int link_status(int argc, char *argv[], void *userdata) {
pager_open_if_enabled();
if (arg_all) {
- _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
_cleanup_free_ LinkInfo *links = NULL;
int c, i;
@@ -902,15 +908,13 @@ static char *lldp_system_caps(uint16_t cap) {
}
static int link_lldp_status(int argc, char *argv[], void *userdata) {
- _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
- _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
_cleanup_free_ LinkInfo *links = NULL;
- const char *state, *word;
-
double ttl = -1;
uint32_t capability;
int i, r, c, j;
- size_t ll;
+ const char *p;
char **s;
pager_open_if_enabled();
@@ -951,14 +955,19 @@ static int link_lldp_status(int argc, char *argv[], void *userdata) {
return -ENOMEM;
STRV_FOREACH(s, l) {
- FOREACH_WORD_QUOTED(word, ll, *s, state) {
- _cleanup_free_ char *t = NULL, *a = NULL, *b = NULL;
- t = strndup(word, ll);
- if (!t)
- return -ENOMEM;
+ p = *s;
+ for (;;) {
+ _cleanup_free_ char *a = NULL, *b = NULL, *word = NULL;
- r = split_pair(t, "=", &a, &b);
+ r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse LLDP syntax \"%s\": %m", *s);
+
+ if (r == 0)
+ break;
+
+ r = split_pair(word, "=", &a, &b);
if (r < 0)
continue;
@@ -1063,9 +1072,7 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case ARG_NO_PAGER:
arg_no_pager = true;
diff --git a/src/network/networkd-address-pool.c b/src/network/networkd-address-pool.c
index d609daafde..889fe1e30d 100644
--- a/src/network/networkd-address-pool.c
+++ b/src/network/networkd-address-pool.c
@@ -19,8 +19,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "networkd.h"
+#include "alloc-util.h"
#include "networkd-address-pool.h"
+#include "networkd.h"
+#include "set.h"
+#include "string-util.h"
int address_pool_new(
Manager *m,
@@ -96,9 +99,10 @@ static bool address_pool_prefix_is_taken(
HASHMAP_FOREACH(l, p->manager->links, i) {
Address *a;
+ Iterator j;
/* Don't clash with assigned addresses */
- LIST_FOREACH(addresses, a, l->addresses) {
+ SET_FOREACH(a, l->addresses, j) {
if (a->family != p->family)
continue;
diff --git a/src/network/networkd-address-pool.h b/src/network/networkd-address-pool.h
index e6207ccce6..7f5bdf1d2f 100644
--- a/src/network/networkd-address-pool.h
+++ b/src/network/networkd-address-pool.h
@@ -23,6 +23,7 @@
typedef struct AddressPool AddressPool;
+#include "in-addr-util.h"
#include "networkd.h"
struct AddressPool {
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c
index b0d296941e..2d475d6ccc 100644
--- a/src/network/networkd-address.c
+++ b/src/network/networkd-address.c
@@ -21,26 +21,39 @@
#include <net/if.h>
-#include "utf8.h"
-#include "util.h"
+#include "alloc-util.h"
#include "conf-parser.h"
#include "firewall-util.h"
#include "netlink-util.h"
-
-#include "networkd.h"
#include "networkd-address.h"
+#include "networkd.h"
+#include "parse-util.h"
+#include "set.h"
+#include "string-util.h"
+#include "utf8.h"
+#include "util.h"
-static void address_init(Address *address) {
- assert(address);
+int address_new(Address **ret) {
+ _cleanup_address_free_ Address *address = NULL;
+
+ address = new0(Address, 1);
+ if (!address)
+ return -ENOMEM;
address->family = AF_UNSPEC;
address->scope = RT_SCOPE_UNIVERSE;
address->cinfo.ifa_prefered = CACHE_INFO_INFINITY_LIFE_TIME;
address->cinfo.ifa_valid = CACHE_INFO_INFINITY_LIFE_TIME;
+
+ *ret = address;
+ address = NULL;
+
+ return 0;
}
int address_new_static(Network *network, unsigned section, Address **ret) {
_cleanup_address_free_ Address *address = NULL;
+ int r;
if (section) {
address = hashmap_get(network->addresses_by_section, UINT_TO_PTR(section));
@@ -52,11 +65,9 @@ int address_new_static(Network *network, unsigned section, Address **ret) {
}
}
- address = new0(Address, 1);
- if (!address)
- return -ENOMEM;
-
- address_init(address);
+ r = address_new(&address);
+ if (r < 0)
+ return r;
address->network = network;
@@ -74,21 +85,6 @@ int address_new_static(Network *network, unsigned section, Address **ret) {
return 0;
}
-int address_new_dynamic(Address **ret) {
- _cleanup_address_free_ Address *address = NULL;
-
- address = new0(Address, 1);
- if (!address)
- return -ENOMEM;
-
- address_init(address);
-
- *ret = address;
- address = NULL;
-
- return 0;
-}
-
void address_free(Address *address) {
if (!address)
return;
@@ -101,10 +97,112 @@ void address_free(Address *address) {
UINT_TO_PTR(address->section));
}
+ if (address->link) {
+ set_remove(address->link->addresses, address);
+ set_remove(address->link->addresses_foreign, address);
+ }
+
free(address);
}
-int address_establish(Address *address, Link *link) {
+static void address_hash_func(const void *b, struct siphash *state) {
+ const Address *a = b;
+
+ assert(a);
+
+ siphash24_compress(&a->family, sizeof(a->family), state);
+
+ switch (a->family) {
+ case AF_INET:
+ siphash24_compress(&a->prefixlen, sizeof(a->prefixlen), state);
+
+ /* peer prefix */
+ if (a->prefixlen != 0) {
+ uint32_t prefix;
+
+ if (a->in_addr_peer.in.s_addr != 0)
+ prefix = be32toh(a->in_addr_peer.in.s_addr) >> (32 - a->prefixlen);
+ else
+ prefix = be32toh(a->in_addr.in.s_addr) >> (32 - a->prefixlen);
+
+ siphash24_compress(&prefix, sizeof(prefix), state);
+ }
+
+ /* fallthrough */
+ case AF_INET6:
+ /* local address */
+ siphash24_compress(&a->in_addr, FAMILY_ADDRESS_SIZE(a->family), state);
+
+ break;
+ default:
+ /* treat any other address family as AF_UNSPEC */
+ break;
+ }
+}
+
+static int address_compare_func(const void *c1, const void *c2) {
+ const Address *a1 = c1, *a2 = c2;
+
+ if (a1->family < a2->family)
+ return -1;
+ if (a1->family > a2->family)
+ return 1;
+
+ switch (a1->family) {
+ /* use the same notion of equality as the kernel does */
+ case AF_INET:
+ if (a1->prefixlen < a2->prefixlen)
+ return -1;
+ if (a1->prefixlen > a2->prefixlen)
+ return 1;
+
+ /* compare the peer prefixes */
+ if (a1->prefixlen != 0) {
+ /* make sure we don't try to shift by 32.
+ * See ISO/IEC 9899:TC3 § 6.5.7.3. */
+ uint32_t b1, b2;
+
+ if (a1->in_addr_peer.in.s_addr != 0)
+ b1 = be32toh(a1->in_addr_peer.in.s_addr) >> (32 - a1->prefixlen);
+ else
+ b1 = be32toh(a1->in_addr.in.s_addr) >> (32 - a1->prefixlen);
+
+ if (a2->in_addr_peer.in.s_addr != 0)
+ b2 = be32toh(a2->in_addr_peer.in.s_addr) >> (32 - a1->prefixlen);
+ else
+ b2 = be32toh(a2->in_addr.in.s_addr) >> (32 - a1->prefixlen);
+
+ if (b1 < b2)
+ return -1;
+ if (b1 > b2)
+ return 1;
+ }
+
+ /* fall-through */
+ case AF_INET6:
+ return memcmp(&a1->in_addr, &a2->in_addr, FAMILY_ADDRESS_SIZE(a1->family));
+ default:
+ /* treat any other address family as AF_UNSPEC */
+ return 0;
+ }
+}
+
+static const struct hash_ops address_hash_ops = {
+ .hash = address_hash_func,
+ .compare = address_compare_func
+};
+
+bool address_equal(Address *a1, Address *a2) {
+ if (a1 == a2)
+ return true;
+
+ if (!a1 || !a2)
+ return false;
+
+ return address_compare_func(a1, a2) == 0;
+}
+
+static int address_establish(Address *address, Link *link) {
bool masq;
int r;
@@ -112,9 +210,9 @@ int address_establish(Address *address, Link *link) {
assert(link);
masq = link->network &&
- link->network->ip_masquerade &&
- address->family == AF_INET &&
- address->scope < RT_SCOPE_LINK;
+ link->network->ip_masquerade &&
+ address->family == AF_INET &&
+ address->scope < RT_SCOPE_LINK;
/* Add firewall entry if this is requested */
if (address->ip_masquerade_done != masq) {
@@ -131,11 +229,88 @@ int address_establish(Address *address, Link *link) {
return 0;
}
-int address_release(Address *address, Link *link) {
+static int address_add_internal(Link *link, Set **addresses,
+ int family,
+ const union in_addr_union *in_addr,
+ unsigned char prefixlen,
+ Address **ret) {
+ _cleanup_address_free_ Address *address = NULL;
int r;
- assert(address);
assert(link);
+ assert(addresses);
+ assert(in_addr);
+
+ r = address_new(&address);
+ if (r < 0)
+ return r;
+
+ address->family = family;
+ address->in_addr = *in_addr;
+ address->prefixlen = prefixlen;
+ /* Consider address tentative until we get the real flags from the kernel */
+ address->flags = IFA_F_TENTATIVE;
+
+ r = set_ensure_allocated(addresses, &address_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = set_put(*addresses, address);
+ if (r < 0)
+ return r;
+
+ address->link = link;
+
+ if (ret)
+ *ret = address;
+
+ address = NULL;
+
+ return 0;
+}
+
+int address_add_foreign(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret) {
+ return address_add_internal(link, &link->addresses_foreign, family, in_addr, prefixlen, ret);
+}
+
+int address_add(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret) {
+ Address *address;
+ int r;
+
+ r = address_get(link, family, in_addr, prefixlen, &address);
+ if (r == -ENOENT) {
+ /* Address does not exist, create a new one */
+ r = address_add_internal(link, &link->addresses, family, in_addr, prefixlen, &address);
+ if (r < 0)
+ return r;
+ } else if (r == 0) {
+ /* Take over a foreign address */
+ r = set_ensure_allocated(&link->addresses, &address_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = set_put(link->addresses, address);
+ if (r < 0)
+ return r;
+
+ set_remove(link->addresses_foreign, address);
+ } else if (r == 1) {
+ /* Already exists, do nothing */
+ ;
+ } else
+ return r;
+
+ if (ret)
+ *ret = address;
+
+ return 0;
+}
+
+static int address_release(Address *address) {
+ int r;
+
+ assert(address);
+ assert(address->link);
/* Remove masquerading firewall entry if it was added */
if (address->ip_masquerade_done) {
@@ -144,7 +319,7 @@ int address_release(Address *address, Link *link) {
r = fw_add_masquerade(false, AF_INET, 0, &masked, address->prefixlen, NULL, NULL, 0);
if (r < 0)
- log_link_warning_errno(link, r, "Failed to disable IP masquerading: %m");
+ log_link_warning_errno(address->link, r, "Failed to disable IP masquerading: %m");
address->ip_masquerade_done = false;
}
@@ -152,81 +327,106 @@ int address_release(Address *address, Link *link) {
return 0;
}
-int address_drop(Address *address, Link *link,
- sd_netlink_message_handler_t callback) {
- _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
+int address_update(Address *address, unsigned char flags, unsigned char scope, struct ifa_cacheinfo *cinfo) {
+ bool ready;
int r;
assert(address);
- assert(address->family == AF_INET || address->family == AF_INET6);
- assert(link);
- assert(link->ifindex > 0);
- assert(link->manager);
- assert(link->manager->rtnl);
+ assert(cinfo);
- address_release(address, link);
+ ready = address_is_ready(address);
- r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_DELADDR,
- link->ifindex, address->family);
- if (r < 0)
- return log_error_errno(r, "Could not allocate RTM_DELADDR message: %m");
+ address->flags = flags;
+ address->scope = scope;
+ address->cinfo = *cinfo;
- r = sd_rtnl_message_addr_set_prefixlen(req, address->prefixlen);
- if (r < 0)
- return log_error_errno(r, "Could not set prefixlen: %m");
+ if (address->link) {
+ link_update_operstate(address->link);
- if (address->family == AF_INET)
- r = sd_netlink_message_append_in_addr(req, IFA_LOCAL, &address->in_addr.in);
- else if (address->family == AF_INET6)
- r = sd_netlink_message_append_in6_addr(req, IFA_LOCAL, &address->in_addr.in6);
- if (r < 0)
- return log_error_errno(r, "Could not append IFA_LOCAL attribute: %m");
+ if (!ready && address_is_ready(address)) {
+ link_check_ready(address->link);
- r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
- if (r < 0)
- return log_error_errno(r, "Could not send rtnetlink message: %m");
+ if (address->family == AF_INET6 &&
+ in_addr_is_link_local(AF_INET6, &address->in_addr) > 0 &&
+ in_addr_is_null(AF_INET6, (const union in_addr_union*) &address->link->ipv6ll_address) > 0) {
+ r = link_ipv6ll_gained(address->link, &address->in_addr.in6);
+ if (r < 0)
+ return r;
+ }
+ }
+ }
- link_ref(link);
+ return 0;
+}
+
+int address_drop(Address *address) {
+ Link *link;
+ bool ready;
+
+ assert(address);
+
+ ready = address_is_ready(address);
+ link = address->link;
+
+ address_release(address);
+ address_free(address);
+
+ link_update_operstate(link);
+
+ if (link && !ready)
+ link_check_ready(link);
return 0;
}
-int address_update(Address *address, Link *link,
- sd_netlink_message_handler_t callback) {
- _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
+int address_get(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret) {
+ Address address = {}, *existing;
+
+ assert(link);
+ assert(in_addr);
+ assert(ret);
+
+ address.family = family;
+ address.in_addr = *in_addr;
+ address.prefixlen = prefixlen;
+
+ existing = set_get(link->addresses, &address);
+ if (existing) {
+ *ret = existing;
+
+ return 1;
+ } else {
+ existing = set_get(link->addresses_foreign, &address);
+ if (!existing)
+ return -ENOENT;
+ }
+
+ *ret = existing;
+
+ return 0;
+}
+
+int address_remove(Address *address, Link *link,
+ sd_netlink_message_handler_t callback) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
int r;
assert(address);
assert(address->family == AF_INET || address->family == AF_INET6);
+ assert(link);
assert(link->ifindex > 0);
assert(link->manager);
assert(link->manager->rtnl);
- r = sd_rtnl_message_new_addr_update(link->manager->rtnl, &req,
+ r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_DELADDR,
link->ifindex, address->family);
if (r < 0)
- return log_error_errno(r, "Could not allocate RTM_NEWADDR message: %m");
+ return log_error_errno(r, "Could not allocate RTM_DELADDR message: %m");
r = sd_rtnl_message_addr_set_prefixlen(req, address->prefixlen);
if (r < 0)
return log_error_errno(r, "Could not set prefixlen: %m");
- 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_netlink_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");
-
if (address->family == AF_INET)
r = sd_netlink_message_append_in_addr(req, IFA_LOCAL, &address->in_addr.in);
else if (address->family == AF_INET6)
@@ -234,22 +434,6 @@ int address_update(Address *address, Link *link,
if (r < 0)
return log_error_errno(r, "Could not append IFA_LOCAL attribute: %m");
- if (address->family == AF_INET) {
- r = sd_netlink_message_append_in_addr(req, IFA_BROADCAST, &address->broadcast);
- if (r < 0)
- return log_error_errno(r, "Could not append IFA_BROADCAST attribute: %m");
- }
-
- if (address->label) {
- r = sd_netlink_message_append_string(req, IFA_LABEL, address->label);
- if (r < 0)
- return log_error_errno(r, "Could not append IFA_LABEL attribute: %m");
- }
-
- r = sd_netlink_message_append_cache_info(req, IFA_CACHEINFO, &address->cinfo);
- if (r < 0)
- return log_error_errno(r, "Could not append IFA_CACHEINFO attribute: %m");
-
r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
if (r < 0)
return log_error_errno(r, "Could not send rtnetlink message: %m");
@@ -292,7 +476,7 @@ static int address_acquire(Link *link, Address *original, Address **ret) {
} else if (original->family == AF_INET6)
in_addr.in6.s6_addr[15] |= 1;
- r = address_new_dynamic(&na);
+ r = address_new(&na);
if (r < 0)
return r;
@@ -318,9 +502,8 @@ static int address_acquire(Link *link, Address *original, Address **ret) {
return 0;
}
-int address_configure(Address *address, Link *link,
- sd_netlink_message_handler_t callback) {
- _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
+int address_configure(Address *address, Link *link, sd_netlink_message_handler_t callback, bool update) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
int r;
assert(address);
@@ -334,8 +517,12 @@ int address_configure(Address *address, Link *link,
if (r < 0)
return r;
- r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_NEWADDR,
- link->ifindex, address->family);
+ if (update)
+ r = sd_rtnl_message_new_addr_update(link->manager->rtnl, &req,
+ link->ifindex, address->family);
+ else
+ r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_NEWADDR,
+ link->ifindex, address->family);
if (r < 0)
return log_error_errno(r, "Could not allocate RTM_NEWADDR message: %m");
@@ -392,13 +579,23 @@ int address_configure(Address *address, Link *link,
if (r < 0)
return log_error_errno(r, "Could not append IFA_CACHEINFO attribute: %m");
- r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
+ r = address_establish(address, link);
if (r < 0)
+ return r;
+
+ r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
+ if (r < 0) {
+ address_release(address);
return log_error_errno(r, "Could not send rtnetlink message: %m");
+ }
link_ref(link);
- address_establish(address, link);
+ r = address_add(link, address->family, &address->in_addr, address->prefixlen, NULL);
+ if (r < 0) {
+ address_release(address);
+ return log_error_errno(r, "Could not add address: %m");
+ }
return 0;
}
@@ -430,15 +627,13 @@ int config_parse_broadcast(
return r;
if (n->family == AF_INET6) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Broadcast is not valid for IPv6 addresses, ignoring assignment: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Broadcast is not valid for IPv6 addresses, ignoring assignment: %s", rvalue);
return 0;
}
r = in_addr_from_string(AF_INET, rvalue, (union in_addr_union*) &n->broadcast);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Broadcast is invalid, ignoring assignment: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Broadcast is invalid, ignoring assignment: %s", rvalue);
return 0;
}
@@ -487,10 +682,10 @@ int config_parse_address(const char *unit,
e = strchr(rvalue, '/');
if (e) {
unsigned i;
+
r = safe_atou(e + 1, &i);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Prefix length is invalid, ignoring assignment: %s", e + 1);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Prefix length is invalid, ignoring assignment: %s", e + 1);
return 0;
}
@@ -502,23 +697,20 @@ int config_parse_address(const char *unit,
r = in_addr_from_string_auto(address, &f, &buffer);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Address is invalid, ignoring assignment: %s", address);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Address is invalid, ignoring assignment: %s", address);
return 0;
}
if (!e && f == AF_INET) {
r = in_addr_default_prefixlen(&buffer.in, &n->prefixlen);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Prefix length not specified, and a default one can not be deduced for '%s', ignoring assignment", address);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Prefix length not specified, and a default one can not be deduced for '%s', ignoring assignment", address);
return 0;
}
}
if (n->family != AF_UNSPEC && f != n->family) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Address is incompatible, ignoring assignment: %s", address);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Address is incompatible, ignoring assignment: %s", address);
return 0;
}
@@ -567,9 +759,7 @@ int config_parse_label(const char *unit,
return log_oom();
if (!ascii_is_valid(label) || strlen(label) >= IFNAMSIZ) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Interface label is not ASCII clean or is too"
- " long, ignoring assignment: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Interface label is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
free(label);
return 0;
}
@@ -587,49 +777,8 @@ int config_parse_label(const char *unit,
return 0;
}
-bool address_equal(Address *a1, Address *a2) {
- /* same object */
- if (a1 == a2)
- return true;
-
- /* one, but not both, is NULL */
- if (!a1 || !a2)
- return false;
-
- if (a1->family != a2->family)
- return false;
-
- switch (a1->family) {
- /* use the same notion of equality as the kernel does */
- case AF_UNSPEC:
- return true;
+bool address_is_ready(const Address *a) {
+ assert(a);
- case AF_INET:
- if (a1->prefixlen != a2->prefixlen)
- return false;
- else if (a1->prefixlen == 0)
- /* make sure we don't try to shift by 32.
- * See ISO/IEC 9899:TC3 § 6.5.7.3. */
- return true;
- else {
- uint32_t b1, b2;
-
- b1 = be32toh(a1->in_addr.in.s_addr);
- b2 = be32toh(a2->in_addr.in.s_addr);
-
- return (b1 >> (32 - a1->prefixlen)) == (b2 >> (32 - a1->prefixlen));
- }
-
- case AF_INET6: {
- uint64_t *b1, *b2;
-
- b1 = (uint64_t*)&a1->in_addr.in6;
- b2 = (uint64_t*)&a2->in_addr.in6;
-
- return (((b1[0] ^ b2[0]) | (b1[1] ^ b2[1])) == 0UL);
- }
-
- default:
- assert_not_reached("Invalid address family");
- }
+ return !(a->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED));
}
diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h
index 39789a2382..accd0a027d 100644
--- a/src/network/networkd-address.h
+++ b/src/network/networkd-address.h
@@ -28,9 +28,9 @@
typedef struct Address Address;
-#include "networkd.h"
-#include "networkd-network.h"
#include "networkd-link.h"
+#include "networkd-network.h"
+#include "networkd.h"
#define CACHE_INFO_INFINITY_LIFE_TIME 0xFFFFFFFFU
@@ -38,6 +38,8 @@ struct Address {
Network *network;
unsigned section;
+ Link *link;
+
int family;
unsigned char prefixlen;
unsigned char scope;
@@ -50,20 +52,23 @@ struct Address {
union in_addr_union in_addr;
union in_addr_union in_addr_peer;
- bool ip_masquerade_done;
+ bool ip_masquerade_done:1;
LIST_FIELDS(Address, addresses);
};
int address_new_static(Network *network, unsigned section, Address **ret);
-int address_new_dynamic(Address **ret);
+int address_new(Address **ret);
void address_free(Address *address);
-int address_configure(Address *address, Link *link, sd_netlink_message_handler_t callback);
-int address_update(Address *address, Link *link, sd_netlink_message_handler_t callback);
-int address_drop(Address *address, Link *link, sd_netlink_message_handler_t callback);
-int address_establish(Address *address, Link *link);
-int address_release(Address *address, Link *link);
+int address_add_foreign(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret);
+int address_add(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret);
+int address_get(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret);
+int address_update(Address *address, unsigned char flags, unsigned char scope, struct ifa_cacheinfo *cinfo);
+int address_drop(Address *address);
+int address_configure(Address *address, Link *link, sd_netlink_message_handler_t callback, bool update);
+int address_remove(Address *address, Link *link, sd_netlink_message_handler_t callback);
bool address_equal(Address *a1, Address *a2);
+bool address_is_ready(const Address *a);
DEFINE_TRIVIAL_CLEANUP_FUNC(Address*, address_free);
#define _cleanup_address_free_ _cleanup_(address_freep)
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index 04f04df117..c7d22876bc 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -22,10 +22,11 @@
#include <netinet/ether.h>
#include <linux/if.h>
+#include "alloc-util.h"
+#include "dhcp-lease-internal.h"
#include "hostname-util.h"
-#include "networkd-link.h"
#include "network-internal.h"
-#include "dhcp-lease-internal.h"
+#include "networkd-link.h"
static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m,
void *userdata) {
@@ -33,7 +34,7 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m,
int r;
assert(link);
- assert(link->dhcp4_messages);
+ assert(link->dhcp4_messages > 0);
link->dhcp4_messages --;
@@ -43,9 +44,9 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m,
link_enter_failed(link);
}
- if (!link->dhcp4_messages) {
+ if (link->dhcp4_messages == 0) {
link->dhcp4_configured = true;
- link_client_handler(link);
+ link_check_ready(link);
}
return 1;
@@ -53,7 +54,7 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m,
static int link_set_dhcp_routes(Link *link) {
struct in_addr gateway;
- struct sd_dhcp_route *static_routes;
+ _cleanup_free_ sd_dhcp_route **static_routes = NULL;
int r, n, i;
assert(link);
@@ -72,11 +73,13 @@ static int link_set_dhcp_routes(Link *link) {
if (r < 0)
return log_link_warning_errno(link, r, "DHCP error: could not get address: %m");
- r = route_new_dynamic(&route, RTPROT_DHCP);
+ r = route_new(&route);
if (r < 0)
return log_link_error_errno(link, r, "Could not allocate route: %m");
- r = route_new_dynamic(&route_gw, RTPROT_DHCP);
+ route->protocol = RTPROT_DHCP;
+
+ r = route_new(&route_gw);
if (r < 0)
return log_link_error_errno(link, r, "Could not allocate route: %m");
@@ -84,11 +87,12 @@ static int link_set_dhcp_routes(Link *link) {
* route for the gw host so that we can route no matter the
* netmask or existing kernel route tables. */
route_gw->family = AF_INET;
- route_gw->dst_addr.in = gateway;
+ route_gw->dst.in = gateway;
route_gw->dst_prefixlen = 32;
- route_gw->prefsrc_addr.in = address;
+ route_gw->prefsrc.in = address;
route_gw->scope = RT_SCOPE_LINK;
- route_gw->metrics = link->network->dhcp_route_metric;
+ route_gw->protocol = RTPROT_DHCP;
+ route_gw->priority = link->network->dhcp_route_metric;
r = route_configure(route_gw, link, &dhcp4_route_handler);
if (r < 0)
@@ -97,9 +101,9 @@ static int link_set_dhcp_routes(Link *link) {
link->dhcp4_messages ++;
route->family = AF_INET;
- route->in_addr.in = gateway;
- route->prefsrc_addr.in = address;
- route->metrics = link->network->dhcp_route_metric;
+ route->gw.in = gateway;
+ route->prefsrc.in = address;
+ route->priority = link->network->dhcp_route_metric;
r = route_configure(route, link, &dhcp4_route_handler);
if (r < 0) {
@@ -120,15 +124,16 @@ static int link_set_dhcp_routes(Link *link) {
for (i = 0; i < n; i++) {
_cleanup_route_free_ Route *route = NULL;
- r = route_new_dynamic(&route, RTPROT_DHCP);
+ r = route_new(&route);
if (r < 0)
return log_link_error_errno(link, r, "Could not allocate route: %m");
route->family = AF_INET;
- route->in_addr.in = static_routes[i].gw_addr;
- route->dst_addr.in = static_routes[i].dst_addr;
- route->dst_prefixlen = static_routes[i].dst_prefixlen;
- route->metrics = link->network->dhcp_route_metric;
+ route->protocol = RTPROT_DHCP;
+ assert_se(sd_dhcp_route_get_gateway(static_routes[i], &route->gw.in) >= 0);
+ assert_se(sd_dhcp_route_get_destination(static_routes[i], &route->dst.in) >= 0);
+ assert_se(sd_dhcp_route_get_destination_prefix_length(static_routes[i], &route->dst_prefixlen) >= 0);
+ route->priority = link->network->dhcp_route_metric;
r = route_configure(route, link, &dhcp4_route_handler);
if (r < 0)
@@ -154,7 +159,7 @@ static int dhcp_lease_lost(Link *link) {
log_link_warning(link, "DHCP lease lost");
if (link->network->dhcp_routes) {
- struct sd_dhcp_route *routes;
+ _cleanup_free_ sd_dhcp_route **routes = NULL;
int n, i;
n = sd_dhcp_lease_get_routes(link->dhcp_lease, &routes);
@@ -162,45 +167,45 @@ static int dhcp_lease_lost(Link *link) {
for (i = 0; i < n; i++) {
_cleanup_route_free_ Route *route = NULL;
- r = route_new_dynamic(&route, RTPROT_UNSPEC);
+ r = route_new(&route);
if (r >= 0) {
route->family = AF_INET;
- route->in_addr.in = routes[i].gw_addr;
- route->dst_addr.in = routes[i].dst_addr;
- route->dst_prefixlen = routes[i].dst_prefixlen;
+ assert_se(sd_dhcp_route_get_gateway(routes[i], &route->gw.in) >= 0);
+ assert_se(sd_dhcp_route_get_destination(routes[i], &route->dst.in) >= 0);
+ assert_se(sd_dhcp_route_get_destination_prefix_length(routes[i], &route->dst_prefixlen) >= 0);
- route_drop(route, link,
- &link_route_drop_handler);
+ route_remove(route, link,
+ &link_route_remove_handler);
}
}
}
}
- r = address_new_dynamic(&address);
+ r = address_new(&address);
if (r >= 0) {
r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway);
if (r >= 0) {
_cleanup_route_free_ Route *route_gw = NULL;
_cleanup_route_free_ Route *route = NULL;
- r = route_new_dynamic(&route_gw, RTPROT_UNSPEC);
+ r = route_new(&route_gw);
if (r >= 0) {
route_gw->family = AF_INET;
- route_gw->dst_addr.in = gateway;
+ route_gw->dst.in = gateway;
route_gw->dst_prefixlen = 32;
route_gw->scope = RT_SCOPE_LINK;
- route_drop(route_gw, link,
- &link_route_drop_handler);
+ route_remove(route_gw, link,
+ &link_route_remove_handler);
}
- r = route_new_dynamic(&route, RTPROT_UNSPEC);
+ r = route_new(&route);
if (r >= 0) {
route->family = AF_INET;
- route->in_addr.in = gateway;
+ route->gw.in = gateway;
- route_drop(route, link,
- &link_route_drop_handler);
+ route_remove(route, link,
+ &link_route_remove_handler);
}
}
@@ -214,7 +219,7 @@ static int dhcp_lease_lost(Link *link) {
address->in_addr.in = addr;
address->prefixlen = prefixlen;
- address_drop(address, link, &link_address_drop_handler);
+ address_remove(address, link, &link_address_remove_handler);
}
}
@@ -250,6 +255,7 @@ static int dhcp_lease_lost(Link *link) {
}
link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease);
+ link_dirty(link);
link->dhcp4_configured = false;
return 0;
@@ -267,7 +273,7 @@ static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m,
log_link_error_errno(link, r, "Could not set DHCPv4 address: %m");
link_enter_failed(link);
} else if (r >= 0)
- link_rtnl_process_address(rtnl, m, link->manager);
+ manager_rtnl_process_address(rtnl, m, link->manager);
link_set_dhcp_routes(link);
@@ -288,7 +294,7 @@ static int dhcp4_update_address(Link *link,
prefixlen = in_addr_netmask_to_prefixlen(netmask);
- r = address_new_dynamic(&addr);
+ r = address_new(&addr);
if (r < 0)
return r;
@@ -299,9 +305,9 @@ static int dhcp4_update_address(Link *link,
addr->prefixlen = prefixlen;
addr->broadcast.s_addr = address->s_addr | ~netmask->s_addr;
- /* use update rather than configure so that we will update the
- * lifetime of an existing address if it has already been configured */
- r = address_update(addr, link, &dhcp4_address_handler);
+ /* allow reusing an existing address and simply update its lifetime
+ * in case it already exists */
+ r = address_configure(addr, link, &dhcp4_address_handler, true);
if (r < 0)
return r;
@@ -326,6 +332,7 @@ static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) {
sd_dhcp_lease_unref(link->dhcp_lease);
link->dhcp4_configured = false;
link->dhcp_lease = sd_dhcp_lease_ref(lease);
+ link_dirty(link);
r = sd_dhcp_lease_get_address(lease, &address);
if (r < 0)
@@ -403,6 +410,7 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
NULL);
link->dhcp_lease = sd_dhcp_lease_ref(lease);
+ link_dirty(link);
if (link->network->dhcp_mtu) {
uint16_t mtu;
@@ -528,9 +536,11 @@ int dhcp4_configure(Link *link) {
assert(link->network);
assert(link->network->dhcp & ADDRESS_FAMILY_IPV4);
- r = sd_dhcp_client_new(&link->dhcp_client);
- if (r < 0)
- return r;
+ if (!link->dhcp_client) {
+ r = sd_dhcp_client_new(&link->dhcp_client);
+ if (r < 0)
+ return r;
+ }
r = sd_dhcp_client_attach_event(link->dhcp_client, NULL, 0);
if (r < 0)
@@ -563,28 +573,28 @@ int dhcp4_configure(Link *link) {
if (link->network->dhcp_mtu) {
r = sd_dhcp_client_set_request_option(link->dhcp_client,
- DHCP_OPTION_INTERFACE_MTU);
+ SD_DHCP_OPTION_INTERFACE_MTU);
if (r < 0)
return r;
}
if (link->network->dhcp_routes) {
r = sd_dhcp_client_set_request_option(link->dhcp_client,
- DHCP_OPTION_STATIC_ROUTE);
+ SD_DHCP_OPTION_STATIC_ROUTE);
if (r < 0)
return r;
r = sd_dhcp_client_set_request_option(link->dhcp_client,
- DHCP_OPTION_CLASSLESS_STATIC_ROUTE);
+ SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE);
if (r < 0)
return r;
}
/* Always acquire the timezone and NTP*/
- r = sd_dhcp_client_set_request_option(link->dhcp_client, DHCP_OPTION_NTP_SERVER);
+ r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_NTP_SERVER);
if (r < 0)
return r;
- r = sd_dhcp_client_set_request_option(link->dhcp_client, DHCP_OPTION_NEW_TZDB_TIMEZONE);
+ r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_NEW_TZDB_TIMEZONE);
if (r < 0)
return r;
diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c
index 13105c7865..e67e51f7ef 100644
--- a/src/network/networkd-dhcp6.c
+++ b/src/network/networkd-dhcp6.c
@@ -22,12 +22,11 @@
#include <netinet/ether.h>
#include <linux/if.h>
-#include "networkd-link.h"
-#include "network-internal.h"
-
-#include "sd-icmp6-nd.h"
#include "sd-dhcp6-client.h"
+#include "network-internal.h"
+#include "networkd-link.h"
+
static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link);
static int dhcp6_lease_information_acquired(sd_dhcp6_client *client,
@@ -53,24 +52,22 @@ static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m,
return 1;
}
- log_link_error(link, "Could not set DHCPv6 address: %s",
- strerror(-r));
+ log_link_error_errno(link, r, "Could not set DHCPv6 address: %m");
link_enter_failed(link);
} else if (r >= 0)
- link_rtnl_process_address(rtnl, m, link->manager);
+ manager_rtnl_process_address(rtnl, m, link->manager);
return 1;
}
-static int dhcp6_address_update(Link *link, struct in6_addr *ip6_addr,
- uint8_t prefixlen, uint32_t lifetime_preferred,
- uint32_t lifetime_valid) {
+static int dhcp6_address_change(Link *link, struct in6_addr *ip6_addr,
+ uint32_t lifetime_preferred, uint32_t lifetime_valid) {
int r;
_cleanup_address_free_ Address *addr = NULL;
- r = address_new_dynamic(&addr);
+ r = address_new(&addr);
if (r < 0)
return r;
@@ -78,17 +75,17 @@ static int dhcp6_address_update(Link *link, struct in6_addr *ip6_addr,
memcpy(&addr->in_addr.in6, ip6_addr, sizeof(*ip6_addr));
addr->flags = IFA_F_NOPREFIXROUTE;
- addr->prefixlen = prefixlen;
+ addr->prefixlen = 128;
addr->cinfo.ifa_prefered = lifetime_preferred;
addr->cinfo.ifa_valid = lifetime_valid;
log_link_info(link,
- "DHCPv6 address "SD_ICMP6_ND_ADDRESS_FORMAT_STR"/%d timeout preferred %d valid %d",
- SD_ICMP6_ND_ADDRESS_FORMAT_VAL(addr->in_addr.in6),
+ "DHCPv6 address "SD_NDISC_ADDRESS_FORMAT_STR"/%d timeout preferred %d valid %d",
+ SD_NDISC_ADDRESS_FORMAT_VAL(addr->in_addr.in6),
addr->prefixlen, lifetime_preferred, lifetime_valid);
- r = address_update(addr, link, dhcp6_address_handler);
+ r = address_configure(addr, link, dhcp6_address_handler, true);
if (r < 0)
log_link_warning_errno(link, r, "Could not assign DHCPv6 address: %m");
@@ -100,7 +97,6 @@ static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link) {
sd_dhcp6_lease *lease;
struct in6_addr ip6_addr;
uint32_t lifetime_preferred, lifetime_valid;
- uint8_t prefixlen;
r = sd_dhcp6_client_get_lease(client, &lease);
if (r < 0)
@@ -112,19 +108,7 @@ static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link) {
&lifetime_preferred,
&lifetime_valid) >= 0) {
- r = sd_icmp6_ra_get_prefixlen(link->icmp6_router_discovery,
- &ip6_addr, &prefixlen);
- if (r < 0 && r != -EADDRNOTAVAIL) {
- log_link_warning(link, "Could not get prefix information: %s",
- strerror(-r));
- return r;
- }
-
- if (r == -EADDRNOTAVAIL)
- prefixlen = 128;
-
- r = dhcp6_address_update(link, &ip6_addr, prefixlen,
- lifetime_preferred, lifetime_valid);
+ r = dhcp6_address_change(link, &ip6_addr, lifetime_preferred, lifetime_valid);
if (r < 0)
return r;
}
@@ -147,7 +131,8 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
case SD_DHCP6_CLIENT_EVENT_STOP:
case SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE:
case SD_DHCP6_CLIENT_EVENT_RETRANS_MAX:
- log_link_warning(link, "DHCPv6 lease lost");
+ if (sd_dhcp6_client_get_lease(client, NULL) >= 0)
+ log_link_warning(link, "DHCPv6 lease lost");
link->dhcp6_configured = false;
break;
@@ -172,211 +157,94 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
default:
if (event < 0)
- log_link_warning(link, "DHCPv6 error: %s",
- strerror(-event));
+ log_link_warning_errno(link, event, "DHCPv6 error: %m");
else
- log_link_warning(link, "DHCPv6 unknown event: %d",
- event);
+ log_link_warning(link, "DHCPv6 unknown event: %d", event);
return;
}
- link_client_handler(link);
+ link_check_ready(link);
}
-static int dhcp6_configure(Link *link, int event) {
- int r;
- bool information_request;
-
- assert_return(link, -EINVAL);
- assert_return(IN_SET(event, SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_TIMEOUT,
- SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_OTHER,
- SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_MANAGED), -EINVAL);
-
- link->dhcp6_configured = false;
-
- if (link->dhcp6_client) {
- r = sd_dhcp6_client_get_information_request(link->dhcp6_client,
- &information_request);
- if (r < 0) {
- log_link_warning(link, "Could not get DHCPv6 Information request setting: %s",
- strerror(-r));
- goto error;
- }
-
- if (information_request && event != SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_OTHER) {
- r = sd_dhcp6_client_stop(link->dhcp6_client);
- if (r < 0) {
- log_link_warning(link, "Could not stop DHCPv6 while setting Managed mode %s",
- strerror(-r));
- goto error;
- }
-
- r = sd_dhcp6_client_set_information_request(link->dhcp6_client,
- false);
- if (r < 0) {
- log_link_warning(link, "Could not unset DHCPv6 Information request: %s",
- strerror(-r));
- goto error;
- }
-
- }
-
- r = sd_dhcp6_client_start(link->dhcp6_client);
- if (r < 0 && r != -EALREADY) {
- log_link_warning(link, "Could not restart DHCPv6: %s",
- strerror(-r));
- goto error;
- }
-
- if (r == -EALREADY)
- link->dhcp6_configured = true;
-
- return r;
- }
-
- r = sd_dhcp6_client_new(&link->dhcp6_client);
- if (r < 0)
- goto error;
+int dhcp6_request_address(Link *link) {
+ int r, inf_req;
+ bool running;
- r = sd_dhcp6_client_attach_event(link->dhcp6_client, NULL, 0);
- if (r < 0)
- goto error;
+ assert(link);
+ assert(link->dhcp6_client);
- r = sd_dhcp6_client_set_mac(link->dhcp6_client,
- (const uint8_t *) &link->mac,
- sizeof (link->mac), ARPHRD_ETHER);
+ r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &inf_req);
if (r < 0)
- goto error;
+ return r;
- r = sd_dhcp6_client_set_index(link->dhcp6_client, link->ifindex);
- if (r < 0)
- goto error;
+ if (!inf_req)
+ return 0;
- r = sd_dhcp6_client_set_callback(link->dhcp6_client, dhcp6_handler,
- link);
+ r = sd_dhcp6_client_is_running(link->dhcp6_client);
if (r < 0)
- goto error;
+ return r;
+ else
+ running = !!r;
- if (event == SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_OTHER) {
- r = sd_dhcp6_client_set_information_request(link->dhcp6_client,
- true);
+ if (running) {
+ r = sd_dhcp6_client_stop(link->dhcp6_client);
if (r < 0)
- goto error;
+ return r;
}
- r = sd_dhcp6_client_start(link->dhcp6_client);
- if (r < 0)
- goto error;
-
- return r;
-
- error:
- link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client);
- 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);
+ r = sd_dhcp6_client_set_information_request(link->dhcp6_client, false);
if (r < 0)
return r;
- log_link_info(link, "IPv6 prefix "SD_ICMP6_ND_ADDRESS_FORMAT_STR"/%d expired",
- SD_ICMP6_ND_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 (running) {
+ r = sd_dhcp6_client_start(link->dhcp6_client);
if (r < 0)
- continue;
-
- log_link_info(link, "IPv6 prefix length updated "SD_ICMP6_ND_ADDRESS_FORMAT_STR"/%d", SD_ICMP6_ND_ADDRESS_FORMAT_VAL(ip6_addr), 128);
-
- dhcp6_address_update(link, &ip6_addr, 128, lifetime_preferred, lifetime_valid);
+ return r;
}
return 0;
}
-static void icmp6_router_handler(sd_icmp6_nd *nd, int event, void *userdata) {
- Link *link = userdata;
+int dhcp6_configure(Link *link) {
+ sd_dhcp6_client *client = NULL;
+ int r;
assert(link);
- assert(link->network);
- assert(link->manager);
-
- if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
- return;
-
- switch(event) {
- case SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_NONE:
- return;
-
- case SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_TIMEOUT:
- case SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_OTHER:
- case SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_MANAGED:
- dhcp6_configure(link, event);
-
- break;
-
- case SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_PREFIX_EXPIRED:
- if (!link->rtnl_extended_attrs)
- dhcp6_prefix_expired(link);
- break;
-
- default:
- if (event < 0)
- log_link_warning(link, "ICMPv6 error: %s",
- strerror(-event));
- else
- log_link_warning(link, "ICMPv6 unknown event: %d",
- event);
-
- break;
- }
-
-}
+ if (link->dhcp6_client)
+ return 0;
-int icmp6_configure(Link *link) {
- int r;
+ r = sd_dhcp6_client_new(&client);
+ if (r < 0)
+ return r;
- assert_return(link, -EINVAL);
+ r = sd_dhcp6_client_attach_event(client, NULL, 0);
+ if (r < 0)
+ goto error;
- r = sd_icmp6_nd_new(&link->icmp6_router_discovery);
+ r = sd_dhcp6_client_set_information_request(client, true);
if (r < 0)
- return r;
+ goto error;
- r = sd_icmp6_nd_attach_event(link->icmp6_router_discovery, NULL, 0);
+ r = sd_dhcp6_client_set_mac(client,
+ (const uint8_t *) &link->mac,
+ sizeof (link->mac), ARPHRD_ETHER);
if (r < 0)
- return r;
+ goto error;
- r = sd_icmp6_nd_set_mac(link->icmp6_router_discovery, &link->mac);
+ r = sd_dhcp6_client_set_index(client, link->ifindex);
if (r < 0)
- return r;
+ goto error;
- r = sd_icmp6_nd_set_index(link->icmp6_router_discovery, link->ifindex);
+ r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link);
if (r < 0)
- return r;
+ goto error;
+
+ link->dhcp6_client = client;
- r = sd_icmp6_nd_set_callback(link->icmp6_router_discovery,
- icmp6_router_handler, link);
+ return 0;
+error:
+ sd_dhcp6_client_unref(client);
return r;
}
diff --git a/src/network/networkd-fdb.c b/src/network/networkd-fdb.c
index 22efadb843..6d819e245b 100644
--- a/src/network/networkd-fdb.c
+++ b/src/network/networkd-fdb.c
@@ -19,15 +19,15 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <net/if.h>
#include <net/ethernet.h>
+#include <net/if.h>
+#include "alloc-util.h"
#include "conf-parser.h"
-#include "util.h"
#include "netlink-util.h"
-
-#include "networkd.h"
#include "networkd-fdb.h"
+#include "networkd.h"
+#include "util.h"
/* create a new FDB entry or get an existing one. */
int fdb_entry_new_static(Network *const network,
@@ -97,7 +97,7 @@ static int set_fdb_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userda
/* send a request to the kernel to add a FDB entry in its static MAC table. */
int fdb_entry_configure(Link *const link, FdbEntry *const fdb_entry) {
- _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
sd_netlink *rtnl;
int r;
@@ -197,7 +197,7 @@ int config_parse_fdb_hwaddr(
&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);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Not a valid MAC address, ignoring assignment: %s", rvalue);
return 0;
}
diff --git a/src/network/networkd-fdb.h b/src/network/networkd-fdb.h
index f0efb902d0..c8e3f2ce56 100644
--- a/src/network/networkd-fdb.h
+++ b/src/network/networkd-fdb.h
@@ -23,8 +23,8 @@
typedef struct FdbEntry FdbEntry;
-#include "networkd.h"
#include "networkd-network.h"
+#include "networkd.h"
struct FdbEntry {
Network *network;
diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c
index af3e3884e6..f4aac4bb93 100644
--- a/src/network/networkd-ipv4ll.c
+++ b/src/network/networkd-ipv4ll.c
@@ -22,8 +22,8 @@
#include <netinet/ether.h>
#include <linux/if.h>
-#include "networkd-link.h"
#include "network-internal.h"
+#include "networkd-link.h"
static int ipv4ll_address_lost(Link *link) {
_cleanup_address_free_ Address *address = NULL;
@@ -42,9 +42,9 @@ static int ipv4ll_address_lost(Link *link) {
log_link_debug(link, "IPv4 link-local release %u.%u.%u.%u", ADDRESS_FMT_VAL(addr));
- r = address_new_dynamic(&address);
+ r = address_new(&address);
if (r < 0) {
- log_link_error(link, "Could not allocate address: %s", strerror(-r));
+ log_link_error_errno(link, r, "Could not allocate address: %m");
return r;
}
@@ -53,22 +53,21 @@ static int ipv4ll_address_lost(Link *link) {
address->prefixlen = 16;
address->scope = RT_SCOPE_LINK;
- address_drop(address, link, &link_address_drop_handler);
+ address_remove(address, link, &link_address_remove_handler);
- r = route_new_dynamic(&route, RTPROT_UNSPEC);
+ r = route_new(&route);
if (r < 0) {
- log_link_error(link, "Could not allocate route: %s",
- strerror(-r));
+ log_link_error_errno(link, r, "Could not allocate route: %m");
return r;
}
route->family = AF_INET;
route->scope = RT_SCOPE_LINK;
- route->metrics = IPV4LL_ROUTE_METRIC;
+ route->priority = IPV4LL_ROUTE_METRIC;
- route_drop(route, link, &link_route_drop_handler);
+ route_remove(route, link, &link_route_remove_handler);
- link_client_handler(link);
+ link_check_ready(link);
return 0;
}
@@ -82,14 +81,14 @@ static int ipv4ll_route_handler(sd_netlink *rtnl, sd_netlink_message *m, void *u
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) {
- log_link_error(link, "could not set ipv4ll route: %s", strerror(-r));
+ log_link_error_errno(link, r, "could not set ipv4ll route: %m");
link_enter_failed(link);
}
link->ipv4ll_route = true;
if (link->ipv4ll_address == true)
- link_client_handler(link);
+ link_check_ready(link);
return 1;
}
@@ -103,15 +102,15 @@ static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, void
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) {
- log_link_error(link, "could not set ipv4ll address: %s", strerror(-r));
+ log_link_error_errno(link, r, "could not set ipv4ll address: %m");
link_enter_failed(link);
} else if (r >= 0)
- link_rtnl_process_address(rtnl, m, link->manager);
+ manager_rtnl_process_address(rtnl, m, link->manager);
link->ipv4ll_address = true;
if (link->ipv4ll_route == true)
- link_client_handler(link);
+ link_check_ready(link);
return 1;
}
@@ -134,7 +133,7 @@ static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) {
log_link_debug(link, "IPv4 link-local claim %u.%u.%u.%u",
ADDRESS_FMT_VAL(address));
- r = address_new_dynamic(&ll_addr);
+ r = address_new(&ll_addr);
if (r < 0)
return r;
@@ -144,19 +143,20 @@ static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) {
ll_addr->broadcast.s_addr = ll_addr->in_addr.in.s_addr | htonl(0xfffffffflu >> ll_addr->prefixlen);
ll_addr->scope = RT_SCOPE_LINK;
- r = address_configure(ll_addr, link, ipv4ll_address_handler);
+ r = address_configure(ll_addr, link, ipv4ll_address_handler, false);
if (r < 0)
return r;
link->ipv4ll_address = false;
- r = route_new_dynamic(&route, RTPROT_STATIC);
+ r = route_new(&route);
if (r < 0)
return r;
route->family = AF_INET;
route->scope = RT_SCOPE_LINK;
- route->metrics = IPV4LL_ROUTE_METRIC;
+ route->protocol = RTPROT_STATIC;
+ route->priority = IPV4LL_ROUTE_METRIC;
r = route_configure(route, link, ipv4ll_route_handler);
if (r < 0)
@@ -201,23 +201,25 @@ static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata){
}
int ipv4ll_configure(Link *link) {
- uint8_t seed[8];
+ uint64_t seed;
int r;
assert(link);
assert(link->network);
assert(link->network->link_local & ADDRESS_FAMILY_IPV4);
- r = sd_ipv4ll_new(&link->ipv4ll);
- if (r < 0)
- return r;
+ if (!link->ipv4ll) {
+ r = sd_ipv4ll_new(&link->ipv4ll);
+ if (r < 0)
+ return r;
+ }
if (link->udev_device) {
- r = net_get_unique_predictable_data(link->udev_device, seed);
+ r = net_get_unique_predictable_data(link->udev_device, &seed);
if (r >= 0) {
assert_cc(sizeof(unsigned) <= 8);
- r = sd_ipv4ll_set_address_seed(link->ipv4ll, *(unsigned *)seed);
+ r = sd_ipv4ll_set_address_seed(link->ipv4ll, (unsigned)seed);
if (r < 0)
return r;
}
diff --git a/src/network/networkd-link-bus.c b/src/network/networkd-link-bus.c
index 1a1524dfb4..4d6ac747fd 100644
--- a/src/network/networkd-link-bus.c
+++ b/src/network/networkd-link-bus.c
@@ -19,11 +19,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "alloc-util.h"
#include "bus-util.h"
-#include "strv.h"
-
-#include "networkd.h"
#include "networkd-link.h"
+#include "networkd.h"
+#include "parse-util.h"
+#include "strv.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_operational_state, link_operstate, LinkOperationalState);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_administrative_state, link_state, LinkState);
@@ -58,15 +59,19 @@ static char *link_bus_path(Link *link) {
int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
_cleanup_strv_free_ char **l = NULL;
Manager *m = userdata;
+ unsigned c = 0;
Link *link;
Iterator i;
- int r;
assert(bus);
assert(path);
assert(m);
assert(nodes);
+ l = new0(char*, hashmap_size(m->links) + 1);
+ if (!l)
+ return -ENOMEM;
+
HASHMAP_FOREACH(link, m->links, i) {
char *p;
@@ -74,11 +79,10 @@ int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***
if (!p)
return -ENOMEM;
- r = strv_consume(&l, p);
- if (r < 0)
- return r;
+ l[c++] = p;
}
+ l[c] = NULL;
*nodes = l;
l = NULL;
@@ -98,10 +102,10 @@ int link_object_find(sd_bus *bus, const char *path, const char *interface, void
assert(found);
r = sd_bus_path_decode(path, "/org/freedesktop/network1/link", &identifier);
- if (r < 0)
+ if (r <= 0)
return 0;
- r = safe_atoi(identifier, &ifindex);
+ r = parse_ifindex(identifier, &ifindex);
if (r < 0)
return 0;
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 5dd14b1104..bbda691c08 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -23,18 +23,22 @@
#include <linux/if.h>
#include <unistd.h>
-#include "util.h"
-#include "virt.h"
-#include "fileio.h"
-#include "socket-util.h"
+#include "alloc-util.h"
#include "bus-util.h"
-#include "udev-util.h"
-#include "netlink-util.h"
#include "dhcp-lease-internal.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "netlink-util.h"
#include "network-internal.h"
-
#include "networkd-link.h"
#include "networkd-netdev.h"
+#include "set.h"
+#include "socket-util.h"
+#include "stdio-util.h"
+#include "string-table.h"
+#include "udev-util.h"
+#include "util.h"
+#include "virt.h"
bool link_dhcp6_enabled(Link *link) {
if (link->flags & IFF_LOOPBACK)
@@ -106,20 +110,56 @@ static bool link_ipv4_forward_enabled(Link *link) {
if (!link->network)
return false;
+ if (link->network->ip_forward == _ADDRESS_FAMILY_BOOLEAN_INVALID)
+ return false;
+
return link->network->ip_forward & ADDRESS_FAMILY_IPV4;
}
static bool link_ipv6_forward_enabled(Link *link) {
+
+ if (!socket_ipv6_is_supported())
+ return false;
+
if (link->flags & IFF_LOOPBACK)
return false;
if (!link->network)
return false;
+ if (link->network->ip_forward == _ADDRESS_FAMILY_BOOLEAN_INVALID)
+ return false;
+
return link->network->ip_forward & ADDRESS_FAMILY_IPV6;
}
+bool link_ipv6_accept_ra_enabled(Link *link) {
+ if (link->flags & IFF_LOOPBACK)
+ return false;
+
+ if (!link->network)
+ return false;
+
+ /* If unset use system default (enabled if local forwarding is disabled.
+ * disabled if local forwarding is enabled).
+ * If set, ignore or enforce RA independent of local forwarding state.
+ */
+ if (link->network->ipv6_accept_ra < 0)
+ /* default to accept RA if ip_forward is disabled and ignore RA if ip_forward is enabled */
+ return !link_ipv6_forward_enabled(link);
+ else if (link->network->ipv6_accept_ra > 0)
+ /* accept RA even if ip_forward is enabled */
+ return true;
+ else
+ /* ignore RA */
+ return false;
+}
+
static IPv6PrivacyExtensions link_ipv6_privacy_extensions(Link *link) {
+
+ if (!socket_ipv6_is_supported())
+ return _IPV6_PRIVACY_EXTENSIONS_INVALID;
+
if (link->flags & IFF_LOOPBACK)
return _IPV6_PRIVACY_EXTENSIONS_INVALID;
@@ -129,6 +169,57 @@ static IPv6PrivacyExtensions link_ipv6_privacy_extensions(Link *link) {
return link->network->ipv6_privacy_extensions;
}
+void link_update_operstate(Link *link) {
+ LinkOperationalState operstate;
+ assert(link);
+
+ if (link->kernel_operstate == IF_OPER_DORMANT)
+ operstate = LINK_OPERSTATE_DORMANT;
+ else if (link_has_carrier(link)) {
+ Address *address;
+ uint8_t scope = RT_SCOPE_NOWHERE;
+ Iterator i;
+
+ /* if we have carrier, check what addresses we have */
+ SET_FOREACH(address, link->addresses, i) {
+ if (!address_is_ready(address))
+ continue;
+
+ if (address->scope < scope)
+ scope = address->scope;
+ }
+
+ /* for operstate we also take foreign addresses into account */
+ SET_FOREACH(address, link->addresses_foreign, i) {
+ if (!address_is_ready(address))
+ continue;
+
+ if (address->scope < scope)
+ scope = address->scope;
+ }
+
+ if (scope < RT_SCOPE_SITE)
+ /* universally accessible addresses found */
+ operstate = LINK_OPERSTATE_ROUTABLE;
+ else if (scope < RT_SCOPE_HOST)
+ /* only link or site local addresses found */
+ operstate = LINK_OPERSTATE_DEGRADED;
+ else
+ /* no useful addresses found */
+ operstate = LINK_OPERSTATE_CARRIER;
+ } else if (link->flags & IFF_UP)
+ operstate = LINK_OPERSTATE_NO_CARRIER;
+ else
+ operstate = LINK_OPERSTATE_OFF;
+
+ if (link->operstate != operstate) {
+ link->operstate = operstate;
+ link_send_changed(link, "OperationalState", NULL);
+ link_dirty(link);
+ manager_dirty(link->manager);
+ }
+}
+
#define FLAG_STRING(string, flag, old, new) \
(((old ^ new) & flag) \
? ((old & flag) ? (" -" string) : (" +" string)) \
@@ -201,7 +292,7 @@ static int link_update_flags(Link *link, sd_netlink_message *m) {
link->flags = flags;
link->kernel_operstate = operstate;
- link_save(link);
+ link_update_operstate(link);
return 0;
}
@@ -291,10 +382,15 @@ static void link_free(Link *link) {
if (!link)
return;
- while ((address = link->addresses)) {
- LIST_REMOVE(addresses, link->addresses, address);
- address_free(address);
- }
+ while (!set_isempty(link->addresses))
+ address_free(set_first(link->addresses));
+
+ while (!set_isempty(link->addresses_foreign))
+ address_free(set_first(link->addresses_foreign));
+
+ link->addresses = set_free(link->addresses);
+
+ link->addresses_foreign = set_free(link->addresses_foreign);
while ((address = link->pool_addresses)) {
LIST_REMOVE(addresses, link->pool_addresses, address);
@@ -307,19 +403,20 @@ static void link_free(Link *link) {
free(link->lease_file);
- sd_lldp_free(link->lldp);
+ sd_lldp_unref(link->lldp);
free(link->lldp_file);
sd_ipv4ll_unref(link->ipv4ll);
sd_dhcp6_client_unref(link->dhcp6_client);
- sd_icmp6_nd_unref(link->icmp6_router_discovery);
+ sd_ndisc_unref(link->ndisc_router_discovery);
if (link->manager)
hashmap_remove(link->manager->links, INT_TO_PTR(link->ifindex));
free(link->ifname);
+ (void)unlink(link->state_file);
free(link->state_file);
udev_device_unref(link->udev_device);
@@ -336,15 +433,28 @@ static void link_free(Link *link) {
}
Link *link_unref(Link *link) {
- if (link && (-- link->n_ref <= 0))
- link_free(link);
+ if (!link)
+ return NULL;
+
+ assert(link->n_ref > 0);
+
+ link->n_ref --;
+
+ if (link->n_ref > 0)
+ return NULL;
+
+ link_free(link);
return NULL;
}
Link *link_ref(Link *link) {
- if (link)
- assert_se(++ link->n_ref >= 2);
+ if (!link)
+ return NULL;
+
+ assert(link->n_ref > 0);
+
+ link->n_ref ++;
return link;
}
@@ -385,7 +495,7 @@ static void link_enter_unmanaged(Link *link) {
link_set_state(link, LINK_STATE_UNMANAGED);
- link_save(link);
+ link_dirty(link);
}
static int link_stop_clients(Link *link) {
@@ -395,9 +505,6 @@ static int link_stop_clients(Link *link) {
assert(link->manager);
assert(link->manager->event);
- if (!link->network)
- return 0;
-
if (link->dhcp_client) {
k = sd_dhcp_client_stop(link->dhcp_client);
if (k < 0)
@@ -410,16 +517,16 @@ static int link_stop_clients(Link *link) {
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)
- r = log_link_warning_errno(link, r, "Could not stop DHCPv6 client: %m");
- }
+ if (link->dhcp6_client) {
+ k = sd_dhcp6_client_stop(link->dhcp6_client);
+ 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 (link->ndisc_router_discovery) {
+ k = sd_ndisc_stop(link->ndisc_router_discovery);
if (k < 0)
- r = log_link_warning_errno(link, r, "Could not stop ICMPv6 router discovery: %m");
+ r = log_link_warning_errno(link, r, "Could not stop IPv6 Router Discovery: %m");
}
if (link->lldp) {
@@ -443,7 +550,7 @@ void link_enter_failed(Link *link) {
link_stop_clients(link);
- link_save(link);
+ link_dirty(link);
}
static Address* link_find_dhcp_server_address(Link *link) {
@@ -484,14 +591,19 @@ static int link_enter_configured(Link *link) {
link_set_state(link, LINK_STATE_CONFIGURED);
- link_save(link);
+ link_dirty(link);
return 0;
}
-void link_client_handler(Link *link) {
+void link_check_ready(Link *link) {
+ Address *a;
+ Iterator i;
+
assert(link);
- assert(link->network);
+
+ if (!link->network)
+ return;
if (!link->static_configured)
return;
@@ -501,12 +613,25 @@ void link_client_handler(Link *link) {
!link->ipv4ll_route)
return;
- if (link_dhcp4_enabled(link) && !link->dhcp4_configured)
+ if (link_ipv6ll_enabled(link))
+ if (in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address) > 0)
return;
- if (link_dhcp6_enabled(link) && !link->dhcp6_configured)
+ if ((link_dhcp4_enabled(link) && !link_dhcp6_enabled(link) &&
+ !link->dhcp4_configured) ||
+ (link_dhcp6_enabled(link) && !link_dhcp4_enabled(link) &&
+ !link->dhcp6_configured) ||
+ (link_dhcp4_enabled(link) && link_dhcp6_enabled(link) &&
+ !link->dhcp4_configured && !link->dhcp6_configured))
return;
+ if (link_ipv6_accept_ra_enabled(link) && !link->ndisc_configured)
+ return;
+
+ SET_FOREACH(a, link->addresses, i)
+ if (!address_is_ready(a))
+ return;
+
if (link->state != LINK_STATE_CONFIGURED)
link_enter_configured(link);
@@ -529,12 +654,12 @@ static int route_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata
r = sd_netlink_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, "Could not set route: %m");
if (link->link_messages == 0) {
log_link_debug(link, "Routes set");
link->static_configured = true;
- link_client_handler(link);
+ link_check_ready(link);
}
return 1;
@@ -563,14 +688,14 @@ static int link_enter_set_routes(Link *link) {
if (link->link_messages == 0) {
link->static_configured = true;
- link_client_handler(link);
+ link_check_ready(link);
} else
log_link_debug(link, "Setting routes");
return 0;
}
-int link_route_drop_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
+int link_route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
_cleanup_link_unref_ Link *link = userdata;
int r;
@@ -583,7 +708,7 @@ int link_route_drop_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userd
r = sd_netlink_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, "Could not drop route: %m");
return 1;
}
@@ -607,9 +732,9 @@ static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userda
r = sd_netlink_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, "could not set address: %m");
else if (r >= 0)
- link_rtnl_process_address(rtnl, m, link->manager);
+ manager_rtnl_process_address(rtnl, m, link->manager);
if (link->link_messages == 0) {
log_link_debug(link, "Addresses set");
@@ -720,7 +845,7 @@ static int link_enter_set_addresses(Link *link) {
link_set_state(link, LINK_STATE_SETTING_ADDRESSES);
LIST_FOREACH(addresses, ad, link->network->static_addresses) {
- r = address_configure(ad, link, &address_handler);
+ r = address_configure(ad, link, &address_handler, false);
if (r < 0) {
log_link_warning_errno(link, r, "Could not set addresses: %m");
link_enter_failed(link);
@@ -852,7 +977,7 @@ static int link_enter_set_addresses(Link *link) {
return 0;
}
-int link_address_drop_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
+int link_address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
_cleanup_link_unref_ Link *link = userdata;
int r;
@@ -865,7 +990,7 @@ int link_address_drop_handler(sd_netlink *rtnl, sd_netlink_message *m, void *use
r = sd_netlink_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, "Could not drop address: %m");
return 1;
}
@@ -1017,13 +1142,13 @@ static int set_mtu_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userda
r = sd_netlink_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, "Could not set MTU: %m");
return 1;
}
int link_set_mtu(Link *link, uint32_t mtu) {
- _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
int r;
assert(link);
@@ -1050,7 +1175,7 @@ int link_set_mtu(Link *link, uint32_t mtu) {
}
static int link_set_bridge(Link *link) {
- _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
int r;
assert(link);
@@ -1127,6 +1252,39 @@ static void lldp_handler(sd_lldp *lldp, int event, void *userdata) {
}
}
+static int link_acquire_ipv6_conf(Link *link) {
+ int r;
+
+ assert(link);
+
+ if (link_dhcp6_enabled(link)) {
+ assert(link->dhcp6_client);
+ assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0);
+
+ log_link_debug(link, "Acquiring DHCPv6 lease");
+
+ r = sd_dhcp6_client_set_local_address(link->dhcp6_client, &link->ipv6ll_address);
+ if (r < 0 && r != -EBUSY)
+ return log_link_warning_errno(link, r, "Could not set IPv6LL address in DHCP client: %m");
+
+ r = sd_dhcp6_client_start(link->dhcp6_client);
+ if (r < 0 && r != -EBUSY)
+ return log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease: %m");
+ }
+
+ if (link_ipv6_accept_ra_enabled(link)) {
+ assert(link->ndisc_router_discovery);
+
+ log_link_debug(link, "Discovering IPv6 routers");
+
+ r = sd_ndisc_router_discovery_start(link->ndisc_router_discovery);
+ if (r < 0 && r != -EBUSY)
+ return log_link_warning_errno(link, r, "Could not start IPv6 Router Discovery: %m");
+ }
+
+ return 0;
+}
+
static int link_acquire_conf(Link *link) {
int r;
@@ -1155,16 +1313,6 @@ static int link_acquire_conf(Link *link) {
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");
-
- r = sd_icmp6_router_solicitation_start(link->icmp6_router_discovery);
- if (r < 0)
- return log_link_warning_errno(link, r, "Could not start IPv6 router discovery: %m");
- }
-
if (link_lldp_enabled(link)) {
assert(link->lldp);
@@ -1205,13 +1353,13 @@ static int link_up_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userda
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);
+ log_link_warning_errno(link, r, "Could not bring up interface: %m");
return 1;
}
static int link_up(Link *link) {
- _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
uint8_t ipv6ll_mode;
int r;
@@ -1292,13 +1440,13 @@ static int link_down_handler(sd_netlink *rtnl, sd_netlink_message *m, void *user
r = sd_netlink_message_get_errno(m);
if (r < 0)
- log_link_warning_errno(link, r, "%-*s: could not bring down interface: %m", IFNAMSIZ, link->ifname);
+ log_link_warning_errno(link, r, "Could not bring down interface: %m");
return 1;
}
static int link_down(Link *link) {
- _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
int r;
assert(link);
@@ -1430,14 +1578,14 @@ static int link_new_bound_by_list(Link *link) {
}
if (list_updated)
- link_save(link);
+ link_dirty(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);
+ link_dirty(carrier);
}
return 0;
@@ -1472,14 +1620,14 @@ static int link_new_bound_to_list(Link *link) {
}
if (list_updated)
- link_save(link);
+ link_dirty(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);
+ link_dirty(carrier);
}
return 0;
@@ -1515,7 +1663,7 @@ static void link_free_bound_to_list(Link *link) {
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);
+ link_dirty(bound_to);
}
return;
@@ -1529,7 +1677,7 @@ static void link_free_bound_by_list(Link *link) {
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_dirty(bound_by);
link_handle_bound_to_list(bound_by);
}
}
@@ -1553,7 +1701,7 @@ static void link_free_carrier_maps(Link *link) {
}
if (list_updated)
- link_save(link);
+ link_dirty(link);
return;
}
@@ -1568,6 +1716,7 @@ void link_drop(Link *link) {
log_link_debug(link, "Link removed");
+ (void)unlink(link->state_file);
link_unref(link);
return;
@@ -1614,7 +1763,7 @@ static int netdev_join_handler(sd_netlink *rtnl, sd_netlink_message *m, void *us
r = sd_netlink_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, "Could not join netdev: %m");
link_enter_failed(link);
return 1;
} else
@@ -1637,7 +1786,7 @@ static int link_enter_join_netdev(Link *link) {
link_set_state(link, LINK_STATE_ENSLAVING);
- link_save(link);
+ link_dirty(link);
if (!link->network->bridge &&
!link->network->bond &&
@@ -1713,32 +1862,69 @@ static int link_enter_join_netdev(Link *link) {
}
static int link_set_ipv4_forward(Link *link) {
- const char *p = NULL, *v;
int r;
- if (link->flags & IFF_LOOPBACK)
+ if (!link_ipv4_forward_enabled(link))
return 0;
- if (link->network->ip_forward == _ADDRESS_FAMILY_BOOLEAN_INVALID)
+ /* We propagate the forwarding flag from one interface to the
+ * global setting one way. This means: as long as at least one
+ * interface was configured at any time that had IP forwarding
+ * enabled the setting will stay on for good. We do this
+ * primarily to keep IPv4 and IPv6 packet forwarding behaviour
+ * somewhat in sync (see below). */
+
+ r = write_string_file("/proc/sys/net/ipv4/ip_forward", "1", WRITE_STRING_FILE_VERIFY_ON_FAILURE);
+ if (r < 0)
+ log_link_warning_errno(link, r, "Cannot turn on IPv4 packet forwarding, ignoring: %m");
+
+ return 0;
+}
+
+static int link_set_ipv6_forward(Link *link) {
+ int r;
+
+ if (!link_ipv6_forward_enabled(link))
return 0;
- p = strjoina("/proc/sys/net/ipv4/conf/", link->ifname, "/forwarding");
- v = one_zero(link_ipv4_forward_enabled(link));
+ /* On Linux, the IPv6 stack does not not know a per-interface
+ * packet forwarding setting: either packet forwarding is on
+ * for all, or off for all. We hence don't bother with a
+ * per-interface setting, but simply propagate the interface
+ * flag, if it is set, to the global flag, one-way. Note that
+ * while IPv4 would allow a per-interface flag, we expose the
+ * same behaviour there and also propagate the setting from
+ * one to all, to keep things simple (see above). */
- r = write_string_file(p, v, 0);
- if (r < 0) {
- /* If the right value is set anyway, don't complain */
- if (verify_one_line_file(p, v) > 0)
- return 0;
+ r = write_string_file("/proc/sys/net/ipv6/conf/all/forwarding", "1", WRITE_STRING_FILE_VERIFY_ON_FAILURE);
+ if (r < 0)
+ log_link_warning_errno(link, r, "Cannot configure IPv6 packet forwarding, ignoring: %m");
- log_link_warning_errno(link, r, "Cannot configure IPv4 forwarding for interface %s: %m", link->ifname);
- }
+ return 0;
+}
+
+static int link_set_ipv6_privacy_extensions(Link *link) {
+ char buf[DECIMAL_STR_MAX(unsigned) + 1];
+ IPv6PrivacyExtensions s;
+ const char *p = NULL;
+ int r;
+
+ s = link_ipv6_privacy_extensions(link);
+ if (s < 0)
+ return 0;
+
+ p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/use_tempaddr");
+ xsprintf(buf, "%u", (unsigned) link->network->ipv6_privacy_extensions);
+
+ r = write_string_file(p, buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE);
+ if (r < 0)
+ log_link_warning_errno(link, r, "Cannot configure IPv6 privacy extension for interface: %m");
return 0;
}
-static int link_set_ipv6_forward(Link *link) {
- const char *p = NULL, *v = NULL;
+static int link_set_ipv6_accept_ra(Link *link) {
+ const char *p = NULL;
int r;
/* Make this a NOP if IPv6 is not available */
@@ -1748,27 +1934,21 @@ static int link_set_ipv6_forward(Link *link) {
if (link->flags & IFF_LOOPBACK)
return 0;
- if (link->network->ip_forward == _ADDRESS_FAMILY_BOOLEAN_INVALID)
+ if (!link->network)
return 0;
- p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/forwarding");
- v = one_zero(link_ipv6_forward_enabled(link));
-
- r = write_string_file(p, v, 0);
- if (r < 0) {
- /* If the right value is set anyway, don't complain */
- if (verify_one_line_file(p, v) > 0)
- return 0;
+ p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/accept_ra");
- log_link_warning_errno(link, r, "Cannot configure IPv6 forwarding for interface: %m");
- }
+ /* We handle router advertisments ourselves, tell the kernel to GTFO */
+ r = write_string_file(p, "0", WRITE_STRING_FILE_VERIFY_ON_FAILURE);
+ if (r < 0)
+ log_link_warning_errno(link, r, "Cannot disable kernel IPv6 accept_ra for interface: %m");
return 0;
}
-static int link_set_ipv6_privacy_extensions(Link *link) {
- char buf[DECIMAL_STR_MAX(unsigned) + 1];
- IPv6PrivacyExtensions s;
+static int link_set_ipv6_dad_transmits(Link *link) {
+ char buf[DECIMAL_STR_MAX(int) + 1];
const char *p = NULL;
int r;
@@ -1776,27 +1956,28 @@ static int link_set_ipv6_privacy_extensions(Link *link) {
if (!socket_ipv6_is_supported())
return 0;
- s = link_ipv6_privacy_extensions(link);
- if (s == _IPV6_PRIVACY_EXTENSIONS_INVALID)
+ if (link->flags & IFF_LOOPBACK)
return 0;
- p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/use_tempaddr");
- xsprintf(buf, "%u", link->network->ipv6_privacy_extensions);
+ if (!link->network)
+ return 0;
- r = write_string_file(p, buf, 0);
- if (r < 0) {
- /* If the right value is set anyway, don't complain */
- if (verify_one_line_file(p, buf) > 0)
- return 0;
+ if (link->network->ipv6_dad_transmits < 0)
+ return 0;
- log_link_warning_errno(link, r, "Cannot configure IPv6 privacy extension for interface: %m");
- }
+ p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/dad_transmits");
+ xsprintf(buf, "%i", link->network->ipv6_dad_transmits);
+
+ r = write_string_file(p, buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE);
+ if (r < 0)
+ log_link_warning_errno(link, r, "Cannot set IPv6 dad transmits for interface: %m");
return 0;
}
-static int link_set_ipv6_accept_ra(Link *link) {
- const char *p = NULL, *v = NULL;
+static int link_set_ipv6_hop_limit(Link *link) {
+ char buf[DECIMAL_STR_MAX(int) + 1];
+ const char *p = NULL;
int r;
/* Make this a NOP if IPv6 is not available */
@@ -1806,29 +1987,46 @@ static int link_set_ipv6_accept_ra(Link *link) {
if (link->flags & IFF_LOOPBACK)
return 0;
- /* If unset use system default (enabled if local forwarding is disabled.
- * disabled if local forwarding is enabled).
- * If set, ignore or enforce RA independent of local forwarding state.
- */
- if (link->network->ipv6_accept_ra < 0) {
- /* default to accept RA if ip_forward is disabled and ignore RA if ip_forward is enabled */
- v = "1";
- } else if (link->network->ipv6_accept_ra > 0) {
- /* "2" means accept RA even if ip_forward is enabled */
- v = "2";
- } else {
- /* "0" means ignore RA */
- v = "0";
+ if (!link->network)
+ return 0;
+
+ if (link->network->ipv6_hop_limit < 0)
+ return 0;
+
+ p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/hop_limit");
+ xsprintf(buf, "%i", link->network->ipv6_hop_limit);
+
+ r = write_string_file(p, buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE);
+ if (r < 0)
+ log_link_warning_errno(link, r, "Cannot set IPv6 hop limit for interface: %m");
+
+ return 0;
+}
+
+static int link_drop_foreign_config(Link *link) {
+ Address *address;
+ Route *route;
+ Iterator i;
+ int r;
+
+ SET_FOREACH(address, link->addresses_foreign, i) {
+ /* we consider IPv6LL addresses to be managed by the kernel */
+ if (address->family == AF_INET6 && in_addr_is_link_local(AF_INET6, &address->in_addr) == 1)
+ continue;
+
+ r = address_remove(address, link, link_address_remove_handler);
+ if (r < 0)
+ return r;
}
- p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/accept_ra");
- r = write_string_file(p, v, 0);
- if (r < 0) {
- /* If the right value is set anyway, don't complain */
- if (verify_one_line_file(p, v) > 0)
- return 0;
+ SET_FOREACH(route, link->routes_foreign, i) {
+ /* do not touch routes managed by the kernel */
+ if (route->protocol == RTPROT_KERNEL)
+ continue;
- log_link_warning_errno(link, r, "Cannot configure IPv6 accept_ra for interface: %m");
+ r = route_remove(route, link, link_address_remove_handler);
+ if (r < 0)
+ return r;
}
return 0;
@@ -1841,6 +2039,14 @@ static int link_configure(Link *link) {
assert(link->network);
assert(link->state == LINK_STATE_PENDING);
+ /* Drop foreign config, but ignore loopback or critical devices.
+ * We do not want to remove loopback address or addresses used for root NFS. */
+ if (!(link->flags & IFF_LOOPBACK) && !(link->network->dhcp_critical)) {
+ r = link_drop_foreign_config(link);
+ if (r < 0)
+ return r;
+ }
+
r = link_set_bridge_fdb(link);
if (r < 0)
return r;
@@ -1861,6 +2067,14 @@ static int link_configure(Link *link) {
if (r < 0)
return r;
+ r = link_set_ipv6_dad_transmits(link);
+ if (r < 0)
+ return r;
+
+ r = link_set_ipv6_hop_limit(link);
+ if (r < 0)
+ return r;
+
if (link_ipv4ll_enabled(link)) {
r = ipv4ll_configure(link);
if (r < 0)
@@ -1883,8 +2097,15 @@ static int link_configure(Link *link) {
return r;
}
- if (link_dhcp6_enabled(link)) {
- r = icmp6_configure(link);
+ if (link_dhcp6_enabled(link) ||
+ link_ipv6_accept_ra_enabled(link)) {
+ r = dhcp6_configure(link);
+ if (r < 0)
+ return r;
+ }
+
+ if (link_ipv6_accept_ra_enabled(link)) {
+ r = ndisc_configure(link);
if (r < 0)
return r;
}
@@ -1908,6 +2129,12 @@ static int link_configure(Link *link) {
r = link_acquire_conf(link);
if (r < 0)
return r;
+
+ if (in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address) == 0) {
+ r = link_acquire_ipv6_conf(link);
+ if (r < 0)
+ return r;
+ }
}
return link_enter_join_netdev(link);
@@ -1936,28 +2163,30 @@ static int link_initialized_and_synced(sd_netlink *rtnl, sd_netlink_message *m,
if (r < 0)
return r;
- r = network_get(link->manager, link->udev_device, link->ifname,
- &link->mac, &network);
- if (r == -ENOENT) {
- link_enter_unmanaged(link);
- return 1;
- } else if (r < 0)
- return r;
+ if (!link->network) {
+ r = network_get(link->manager, link->udev_device, link->ifname,
+ &link->mac, &network);
+ if (r == -ENOENT) {
+ link_enter_unmanaged(link);
+ return 1;
+ } else if (r < 0)
+ return r;
- if (link->flags & IFF_LOOPBACK) {
- if (network->link_local != ADDRESS_FAMILY_NO)
- log_link_debug(link, "Ignoring link-local autoconfiguration for loopback link");
+ if (link->flags & IFF_LOOPBACK) {
+ if (network->link_local != ADDRESS_FAMILY_NO)
+ 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");
+ if (network->dhcp != ADDRESS_FAMILY_NO)
+ log_link_debug(link, "Ignoring DHCP clients for loopback link");
- if (network->dhcp_server)
- log_link_debug(link, "Ignoring DHCP server for loopback link");
- }
+ if (network->dhcp_server)
+ log_link_debug(link, "Ignoring DHCP server for loopback link");
+ }
- r = network_apply(link->manager, network, link);
- if (r < 0)
- return r;
+ r = network_apply(link->manager, network, link);
+ if (r < 0)
+ return r;
+ }
r = link_new_bound_to_list(link);
if (r < 0)
@@ -1971,7 +2200,7 @@ static int link_initialized_and_synced(sd_netlink *rtnl, sd_netlink_message *m,
}
int link_initialized(Link *link, struct udev_device *device) {
- _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
int r;
assert(link);
@@ -2009,177 +2238,195 @@ int link_initialized(Link *link, struct udev_device *device) {
return 0;
}
-static Address* link_get_equal_address(Link *link, Address *needle) {
- Address *i;
+static int link_load(Link *link) {
+ _cleanup_free_ char *network_file = NULL,
+ *addresses = NULL,
+ *routes = NULL,
+ *dhcp4_address = NULL,
+ *ipv4ll_address = NULL;
+ union in_addr_union address;
+ union in_addr_union route_dst;
+ const char *p;
+ int r;
assert(link);
- assert(needle);
- LIST_FOREACH(addresses, i, link->addresses)
- if (address_equal(i, needle))
- return i;
-
- return NULL;
-}
-
-int link_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) {
- Manager *m = userdata;
- 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;
- int r, ifindex;
+ r = parse_env_file(link->state_file, NEWLINE,
+ "NETWORK_FILE", &network_file,
+ "ADDRESSES", &addresses,
+ "ROUTES", &routes,
+ "DHCP4_ADDRESS", &dhcp4_address,
+ "IPV4LL_ADDRESS", &ipv4ll_address,
+ NULL);
+ if (r < 0 && r != -ENOENT)
+ return log_link_error_errno(link, r, "Failed to read %s: %m", link->state_file);
+
+ if (network_file) {
+ Network *network;
+ char *suffix;
+
+ /* drop suffix */
+ suffix = strrchr(network_file, '.');
+ if (!suffix) {
+ log_link_debug(link, "Failed to get network name from %s", network_file);
+ goto network_file_fail;
+ }
+ *suffix = '\0';
- assert(rtnl);
- assert(message);
- assert(m);
+ r = network_get_by_name(link->manager, basename(network_file), &network);
+ if (r < 0) {
+ log_link_debug_errno(link, r, "Failed to get network %s: %m", basename(network_file));
+ goto network_file_fail;
+ }
- if (sd_netlink_message_is_error(message)) {
- r = sd_netlink_message_get_errno(message);
+ r = network_apply(link->manager, network, link);
if (r < 0)
- log_warning_errno(r, "rtnl: failed to receive address: %m");
-
- return 0;
+ return log_link_error_errno(link, r, "Failed to apply network %s: %m", basename(network_file));
}
- r = sd_netlink_message_get_type(message, &type);
- if (r < 0) {
- 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 from address: %m");
- return 0;
- } else if (ifindex <= 0) {
- log_warning("rtnl: received address message with invalid ifindex: %d", ifindex);
- return 0;
- } else {
- r = link_get(m, ifindex, &link);
- if (r < 0 || !link) {
- /* when enumerating we might be out of sync, but we will
- * get the address again, so just ignore it */
- if (!m->enumerating)
- log_warning("rtnl: received address for nonexistent link (%d), ignoring", ifindex);
- return 0;
- }
- }
+network_file_fail:
- r = address_new_dynamic(&address);
- if (r < 0)
- return r;
+ if (addresses) {
+ p = addresses;
- 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.");
- return 0;
- }
+ for (;;) {
+ _cleanup_free_ char *address_str = NULL;
+ char *prefixlen_str;
+ int family;
+ unsigned char prefixlen;
- r = sd_rtnl_message_addr_get_prefixlen(message, &address->prefixlen);
- if (r < 0) {
- log_link_warning_errno(link, r, "rtnl: received address with invalid prefixlen, ignoring: %m");
- return 0;
- }
+ r = extract_first_word(&p, &address_str, NULL, 0);
+ if (r < 0) {
+ log_link_debug_errno(link, r, "Failed to extract next address string: %m");
+ continue;
+ }
+ if (r == 0)
+ break;
- r = sd_rtnl_message_addr_get_scope(message, &address->scope);
- if (r < 0) {
- log_link_warning_errno(link, r, "rtnl: received address with invalid scope, ignoring: %m");
- return 0;
- }
+ prefixlen_str = strchr(address_str, '/');
+ if (!prefixlen_str) {
+ log_link_debug(link, "Failed to parse address and prefix length %s", address_str);
+ continue;
+ }
- r = sd_rtnl_message_addr_get_flags(message, &flags);
- if (r < 0) {
- log_link_warning_errno(link, r, "rtnl: received address with invalid flags, ignoring: %m");
- return 0;
- }
- address->flags = flags;
+ *prefixlen_str ++ = '\0';
- switch (address->family) {
- case AF_INET:
- r = sd_netlink_message_read_in_addr(message, IFA_LOCAL, &address->in_addr.in);
- if (r < 0) {
- log_link_warning_errno(link, r, "rtnl: received address without valid address, ignoring: %m");
- return 0;
- }
+ r = sscanf(prefixlen_str, "%hhu", &prefixlen);
+ if (r != 1) {
+ log_link_error(link, "Failed to parse prefixlen %s", prefixlen_str);
+ continue;
+ }
- break;
+ r = in_addr_from_string_auto(address_str, &family, &address);
+ if (r < 0) {
+ log_link_debug_errno(link, r, "Failed to parse address %s: %m", address_str);
+ continue;
+ }
- case AF_INET6:
- r = sd_netlink_message_read_in6_addr(message, IFA_ADDRESS, &address->in_addr.in6);
- if (r < 0) {
- log_link_warning_errno(link, r, "rtnl: received address without valid address, ignoring: %m");
- return 0;
+ r = address_add(link, family, &address, prefixlen, NULL);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to add address: %m");
}
-
- break;
-
- default:
- assert_not_reached("invalid address family");
}
- if (!inet_ntop(address->family, &address->in_addr, buf, INET6_ADDRSTRLEN)) {
- log_link_warning(link, "Could not print address");
- return 0;
- }
+ if (routes) {
+ p = routes;
- r = sd_netlink_message_read_cache_info(message, IFA_CACHEINFO, &address->cinfo);
- if (r >= 0) {
- if (address->cinfo.ifa_valid == CACHE_INFO_INFINITY_LIFE_TIME)
- valid_str = "ever";
- else
- valid_str = format_timespan(valid_buf, FORMAT_TIMESPAN_MAX,
- address->cinfo.ifa_valid * USEC_PER_SEC,
- USEC_PER_SEC);
- }
+ for (;;) {
+ Route *route;
+ _cleanup_free_ char *route_str = NULL;
+ _cleanup_(sd_event_source_unrefp) sd_event_source *expire = NULL;
+ usec_t lifetime;
+ char *prefixlen_str;
+ int family;
+ unsigned char prefixlen, tos, table;
+ uint32_t priority;
+
+ r = extract_first_word(&p, &route_str, NULL, 0);
+ if (r < 0) {
+ log_link_debug_errno(link, r, "Failed to extract next route string: %m");
+ continue;
+ }
+ if (r == 0)
+ break;
- existing = link_get_equal_address(link, address);
+ prefixlen_str = strchr(route_str, '/');
+ if (!prefixlen_str) {
+ log_link_debug(link, "Failed to parse route %s", route_str);
+ continue;
+ }
- switch (type) {
- case RTM_NEWADDR:
- if (existing) {
- log_link_debug(link, "Updating address: %s/%u (valid for %s)", buf, address->prefixlen, valid_str);
+ *prefixlen_str ++ = '\0';
+ r = sscanf(prefixlen_str, "%hhu/%hhu/%"SCNu32"/%hhu/"USEC_FMT, &prefixlen, &tos, &priority, &table, &lifetime);
+ if (r != 5) {
+ log_link_debug(link,
+ "Failed to parse destination prefix length, tos, priority, table or expiration %s",
+ prefixlen_str);
+ continue;
+ }
- existing->scope = address->scope;
- existing->flags = address->flags;
- existing->cinfo = address->cinfo;
+ r = in_addr_from_string_auto(route_str, &family, &route_dst);
+ if (r < 0) {
+ log_link_debug_errno(link, r, "Failed to parse route destination %s: %m", route_str);
+ continue;
+ }
- } else {
- log_link_debug(link, "Adding address: %s/%u (valid for %s)", buf, address->prefixlen, valid_str);
+ r = route_add(link, family, &route_dst, prefixlen, tos, priority, table, &route);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to add route: %m");
- LIST_PREPEND(addresses, link->addresses, address);
- address_establish(address, link);
+ if (lifetime != USEC_INFINITY) {
+ r = sd_event_add_time(link->manager->event, &expire, clock_boottime_or_monotonic(), lifetime,
+ 0, route_expire_handler, route);
+ if (r < 0)
+ log_link_warning_errno(link, r, "Could not arm route expiration handler: %m");
+ }
- address = NULL;
+ route->lifetime = lifetime;
+ sd_event_source_unref(route->expire);
+ route->expire = expire;
+ expire = NULL;
+ }
+ }
- link_save(link);
+ if (dhcp4_address) {
+ r = in_addr_from_string(AF_INET, dhcp4_address, &address);
+ if (r < 0) {
+ log_link_debug_errno(link, r, "Falied to parse DHCPv4 address %s: %m", dhcp4_address);
+ goto dhcp4_address_fail;
}
- break;
+ r = sd_dhcp_client_new(&link->dhcp_client);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Falied to create DHCPv4 client: %m");
- case RTM_DELADDR:
+ r = sd_dhcp_client_set_request_address(link->dhcp_client, &address.in);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Falied to set inital DHCPv4 address %s: %m", dhcp4_address);
+ }
- if (existing) {
- log_link_debug(link, "Removing address: %s/%u (valid for %s)", buf, address->prefixlen, valid_str);
- address_release(existing, link);
- LIST_REMOVE(addresses, link->addresses, existing);
- address_free(existing);
- } else
- log_link_warning(link, "Removing non-existent address: %s/%u (valid for %s)", buf, address->prefixlen, valid_str);
+dhcp4_address_fail:
- break;
- default:
- assert_not_reached("Received invalid RTNL message type");
+ if (ipv4ll_address) {
+ r = in_addr_from_string(AF_INET, ipv4ll_address, &address);
+ if (r < 0) {
+ log_link_debug_errno(link, r, "Falied to parse IPv4LL address %s: %m", ipv4ll_address);
+ goto ipv4ll_address_fail;
+ }
+
+ r = sd_ipv4ll_new(&link->ipv4ll);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Falied to create IPv4LL client: %m");
+
+ r = sd_ipv4ll_set_address(link->ipv4ll, &address.in);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Falied to set inital IPv4LL address %s: %m", ipv4ll_address);
}
- return 1;
+ipv4ll_address_fail:
+
+ return 0;
}
int link_add(Manager *m, sd_netlink_message *message, Link **ret) {
@@ -2201,12 +2448,18 @@ int link_add(Manager *m, sd_netlink_message *message, Link **ret) {
log_link_debug(link, "Link %d added", link->ifindex);
+ r = link_load(link);
+ if (r < 0)
+ return r;
+
if (detect_container() <= 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)
- return log_link_warning_errno(link, errno, "Could not find udev device: %m");
+ if (!device) {
+ r = log_link_warning_errno(link, errno, "Could not find udev device: %m");
+ goto failed;
+ }
if (udev_device_get_is_initialized(device) <= 0) {
/* not yet ready */
@@ -2216,14 +2469,38 @@ int link_add(Manager *m, sd_netlink_message *message, Link **ret) {
r = link_initialized(link, device);
if (r < 0)
- return r;
+ goto failed;
} else {
/* we are calling a callback directly, so must take a ref */
link_ref(link);
r = link_initialized_and_synced(m->rtnl, NULL, link);
if (r < 0)
+ goto failed;
+ }
+
+ return 0;
+failed:
+ link_enter_failed(link);
+ return r;
+}
+
+int link_ipv6ll_gained(Link *link, const struct in6_addr *address) {
+ int r;
+
+ assert(link);
+
+ log_link_info(link, "Gained IPv6LL");
+
+ link->ipv6ll_address = *address;
+ link_check_ready(link);
+
+ if (!IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_UNMANAGED, LINK_STATE_FAILED)) {
+ r = link_acquire_ipv6_conf(link);
+ if (r < 0) {
+ link_enter_failed(link);
return r;
+ }
}
return 0;
@@ -2234,7 +2511,7 @@ static int link_carrier_gained(Link *link) {
assert(link);
- if (link->network) {
+ if (!IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_UNMANAGED, LINK_STATE_FAILED)) {
r = link_acquire_conf(link);
if (r < 0) {
link_enter_failed(link);
@@ -2414,49 +2691,13 @@ int link_update(Link *link, sd_netlink_message *m) {
return 0;
}
-static void link_update_operstate(Link *link) {
- LinkOperationalState operstate;
- assert(link);
-
- if (link->kernel_operstate == IF_OPER_DORMANT)
- operstate = LINK_OPERSTATE_DORMANT;
- else if (link_has_carrier(link)) {
- Address *address;
- uint8_t scope = RT_SCOPE_NOWHERE;
-
- /* if we have carrier, check what addresses we have */
- LIST_FOREACH(addresses, address, link->addresses) {
- if (address->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED))
- continue;
-
- if (address->scope < scope)
- scope = address->scope;
- }
-
- if (scope < RT_SCOPE_SITE)
- /* universally accessible addresses found */
- operstate = LINK_OPERSTATE_ROUTABLE;
- else if (scope < RT_SCOPE_HOST)
- /* only link or site local addresses found */
- operstate = LINK_OPERSTATE_DEGRADED;
- else
- /* no useful addresses found */
- operstate = LINK_OPERSTATE_CARRIER;
- } else if (link->flags & IFF_UP)
- operstate = LINK_OPERSTATE_NO_CARRIER;
- else
- operstate = LINK_OPERSTATE_OFF;
-
- if (link->operstate != operstate) {
- link->operstate = operstate;
- link_send_changed(link, "OperationalState", NULL);
- }
-}
-
int link_save(Link *link) {
_cleanup_free_ char *temp_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
const char *admin_state, *oper_state;
+ Address *a;
+ Route *route;
+ Iterator i;
int r;
assert(link);
@@ -2464,12 +2705,6 @@ int link_save(Link *link) {
assert(link->lease_file);
assert(link->manager);
- link_update_operstate(link);
-
- r = manager_save(link->manager);
- if (r < 0)
- return r;
-
if (link->state == LINK_STATE_LINGER) {
unlink(link->state_file);
return 0;
@@ -2499,9 +2734,8 @@ int link_save(Link *link) {
sd_dhcp6_lease *dhcp6_lease = NULL;
if (link->dhcp6_client) {
- r = sd_dhcp6_client_get_lease(link->dhcp6_client,
- &dhcp6_lease);
- if (r < 0)
+ r = sd_dhcp6_client_get_lease(link->dhcp6_client, &dhcp6_lease);
+ if (r < 0 && r != -ENOMSG)
log_link_debug(link, "No DHCPv6 lease");
}
@@ -2537,13 +2771,12 @@ int link_save(Link *link) {
if (space)
fputc(' ', f);
serialize_in6_addrs(f, in6_addrs, r);
- space = true;
}
}
- fputs("\n", f);
+ fputc('\n', f);
- fprintf(f, "NTP=");
+ fputs("NTP=", f);
space = false;
STRV_FOREACH(address, link->network->ntp) {
if (space)
@@ -2590,9 +2823,9 @@ int link_save(Link *link) {
}
}
- fputs("\n", f);
+ fputc('\n', f);
- fprintf(f, "DOMAINS=");
+ fputs("DOMAINS=", f);
space = false;
STRV_FOREACH(domain, link->network->domains) {
if (space)
@@ -2628,18 +2861,67 @@ int link_save(Link *link) {
}
}
- fputs("\n", f);
+ fputc('\n', f);
fprintf(f, "WILDCARD_DOMAIN=%s\n",
yes_no(link->network->wildcard_domain));
fprintf(f, "LLMNR=%s\n",
resolve_support_to_string(link->network->llmnr));
+ fprintf(f, "MDNS=%s\n",
+ resolve_support_to_string(link->network->mdns));
+
+ if (link->network->dnssec_mode != _DNSSEC_MODE_INVALID)
+ fprintf(f, "DNSSEC=%s\n",
+ dnssec_mode_to_string(link->network->dnssec_mode));
+
+ if (!set_isempty(link->network->dnssec_negative_trust_anchors)) {
+ const char *n;
+
+ fputs("DNSSEC_NTA=", f);
+ space = false;
+ SET_FOREACH(n, link->network->dnssec_negative_trust_anchors, i) {
+ if (space)
+ fputc(' ', f);
+ fputs(n, f);
+ space = true;
+ }
+ fputc('\n', f);
+ }
+
+ fputs("ADDRESSES=", f);
+ space = false;
+ SET_FOREACH(a, link->addresses, i) {
+ _cleanup_free_ char *address_str = NULL;
+
+ r = in_addr_to_string(a->family, &a->in_addr, &address_str);
+ if (r < 0)
+ goto fail;
+
+ fprintf(f, "%s%s/%u", space ? " " : "", address_str, a->prefixlen);
+ space = true;
+ }
+ fputc('\n', f);
+
+ fputs("ROUTES=", f);
+ space = false;
+ SET_FOREACH(route, link->routes, i) {
+ _cleanup_free_ char *route_str = NULL;
+
+ r = in_addr_to_string(route->family, &route->dst, &route_str);
+ if (r < 0)
+ goto fail;
+
+ fprintf(f, "%s%s/%hhu/%hhu/%"PRIu32"/%hhu/"USEC_FMT, space ? " " : "", route_str,
+ route->dst_prefixlen, route->tos, route->priority, route->table, route->lifetime);
+ space = true;
+ }
+
+ fputc('\n', f);
}
if (!hashmap_isempty(link->bound_to_links)) {
Link *carrier;
- Iterator i;
bool space = false;
fputs("CARRIER_BOUND_TO=", f);
@@ -2650,12 +2932,11 @@ int link_save(Link *link) {
space = true;
}
- fputs("\n", f);
+ fputc('\n', f);
}
if (!hashmap_isempty(link->bound_by_links)) {
Link *carrier;
- Iterator i;
bool space = false;
fputs("CARRIER_BOUND_BY=", f);
@@ -2666,19 +2947,25 @@ int link_save(Link *link) {
space = true;
}
- fputs("\n", f);
+ fputc('\n', f);
}
if (link->dhcp_lease) {
+ struct in_addr address;
const char *tz = NULL;
+ assert(link->network);
+
r = sd_dhcp_lease_get_timezone(link->dhcp_lease, &tz);
if (r >= 0)
fprintf(f, "TIMEZONE=%s\n", tz);
- }
- if (link->dhcp_lease) {
- assert(link->network);
+ r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
+ if (r >= 0) {
+ fputs("DHCP4_ADDRESS=", f);
+ serialize_in_addrs(f, &address, 1);
+ fputc('\n', f);
+ }
r = dhcp_lease_save(link->dhcp_lease, link->lease_file);
if (r < 0)
@@ -2690,6 +2977,17 @@ int link_save(Link *link) {
} else
unlink(link->lease_file);
+ if (link->ipv4ll) {
+ struct in_addr address;
+
+ r = sd_ipv4ll_get_address(link->ipv4ll, &address);
+ if (r >= 0) {
+ fputs("IPV4LL_ADDRESS=", f);
+ serialize_in_addrs(f, &address, 1);
+ fputc('\n', f);
+ }
+ }
+
if (link->lldp) {
assert(link->network);
@@ -2722,6 +3020,34 @@ fail:
return log_link_error_errno(link, r, "Failed to save link data to %s: %m", link->state_file);
}
+/* The serialized state in /run is no longer up-to-date. */
+void link_dirty(Link *link) {
+ int r;
+
+ assert(link);
+
+ r = set_ensure_allocated(&link->manager->dirty_links, NULL);
+ if (r < 0)
+ /* allocation errors are ignored */
+ return;
+
+ r = set_put(link->manager->dirty_links, link);
+ if (r < 0)
+ /* allocation errors are ignored */
+ return;
+
+ link_ref(link);
+}
+
+/* The serialized state in /run is up-to-date */
+void link_clean(Link *link) {
+ assert(link);
+ assert(link->manager);
+
+ set_remove(link->manager->dirty_links, link);
+ link_unref(link);
+}
+
static const char* const link_state_table[_LINK_STATE_MAX] = {
[LINK_STATE_PENDING] = "pending",
[LINK_STATE_ENSLAVING] = "configuring",
diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
index 7b219c6854..3964a12f37 100644
--- a/src/network/networkd-link.h
+++ b/src/network/networkd-link.h
@@ -25,10 +25,10 @@
#include "sd-dhcp-client.h"
#include "sd-dhcp-server.h"
-#include "sd-ipv4ll.h"
-#include "sd-icmp6-nd.h"
#include "sd-dhcp6-client.h"
+#include "sd-ipv4ll.h"
#include "sd-lldp.h"
+#include "sd-ndisc.h"
typedef struct Link Link;
@@ -56,9 +56,9 @@ typedef enum LinkOperationalState {
_LINK_OPERSTATE_INVALID = -1
} LinkOperationalState;
-#include "networkd.h"
-#include "networkd-network.h"
#include "networkd-address.h"
+#include "networkd-network.h"
+#include "networkd.h"
struct Link {
Manager *manager;
@@ -69,6 +69,7 @@ struct Link {
char *ifname;
char *state_file;
struct ether_addr mac;
+ struct in6_addr ipv6ll_address;
uint32_t mtu;
struct udev_device *udev_device;
@@ -83,7 +84,10 @@ struct Link {
unsigned link_messages;
unsigned enslaving;
- LIST_HEAD(Address, addresses);
+ Set *addresses;
+ Set *addresses_foreign;
+ Set *routes;
+ Set *routes_foreign;
sd_dhcp_client *dhcp_client;
sd_dhcp_lease *dhcp_lease;
@@ -92,10 +96,12 @@ struct Link {
unsigned dhcp4_messages;
bool dhcp4_configured;
bool dhcp6_configured;
+ unsigned ndisc_messages;
+ bool ndisc_configured;
sd_ipv4ll *ipv4ll;
- bool ipv4ll_address;
- bool ipv4ll_route;
+ bool ipv4ll_address:1;
+ bool ipv4ll_route:1;
bool static_configured;
@@ -103,7 +109,7 @@ struct Link {
sd_dhcp_server *dhcp_server;
- sd_icmp6_nd *icmp6_router_discovery;
+ sd_ndisc *ndisc_router_discovery;
sd_dhcp6_client *dhcp6_client;
bool rtnl_extended_attrs;
@@ -120,29 +126,35 @@ int link_get(Manager *m, int ifindex, Link **ret);
int link_add(Manager *manager, sd_netlink_message *message, Link **ret);
void link_drop(Link *link);
-int link_address_drop_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata);
-int link_route_drop_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata);
+int link_address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata);
+int link_route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata);
void link_enter_failed(Link *link);
int link_initialized(Link *link, struct udev_device *device);
-void link_client_handler(Link *link);
+void link_check_ready(Link *link);
+void link_update_operstate(Link *link);
int link_update(Link *link, sd_netlink_message *message);
-int link_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, void *userdata);
+void link_dirty(Link *link);
+void link_clean(Link *link);
int link_save(Link *link);
int link_carrier_reset(Link *link);
bool link_has_carrier(Link *link);
+int link_ipv6ll_gained(Link *link, const struct in6_addr *address);
+
int link_set_mtu(Link *link, uint32_t mtu);
int link_set_hostname(Link *link, const char *hostname);
int link_set_timezone(Link *link, const char *timezone);
int ipv4ll_configure(Link *link);
int dhcp4_configure(Link *link);
-int icmp6_configure(Link *link);
+int dhcp6_configure(Link *link);
+int dhcp6_request_address(Link *link);
+int ndisc_configure(Link *link);
bool link_lldp_enabled(Link *link);
bool link_ipv4ll_enabled(Link *link);
@@ -150,6 +162,7 @@ bool link_ipv6ll_enabled(Link *link);
bool link_dhcp4_server_enabled(Link *link);
bool link_dhcp4_enabled(Link *link);
bool link_dhcp6_enabled(Link *link);
+bool link_ipv6_accept_ra_enabled(Link *link);
const char* link_state_to_string(LinkState s) _const_;
LinkState link_state_from_string(const char *s) _pure_;
diff --git a/src/network/networkd-manager-bus.c b/src/network/networkd-manager-bus.c
index b281f4fdb6..dafaf2daea 100644
--- a/src/network/networkd-manager-bus.c
+++ b/src/network/networkd-manager-bus.c
@@ -19,8 +19,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "alloc-util.h"
#include "bus-util.h"
-
#include "networkd.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_operational_state, link_operstate, LinkOperationalState);
diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c
index b4259cafef..24f5304cb0 100644
--- a/src/network/networkd-manager.c
+++ b/src/network/networkd-manager.c
@@ -22,21 +22,23 @@
#include <sys/socket.h>
#include <linux/if.h>
-#include "sd-netlink.h"
#include "sd-daemon.h"
+#include "sd-netlink.h"
-#include "conf-parser.h"
-#include "path-util.h"
-#include "libudev-private.h"
-#include "udev-util.h"
-#include "netlink-util.h"
+#include "alloc-util.h"
#include "bus-util.h"
+#include "conf-parser.h"
#include "def.h"
-#include "virt.h"
-#include "set.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "libudev-private.h"
#include "local-addresses.h"
-
+#include "netlink-util.h"
#include "networkd.h"
+#include "path-util.h"
+#include "set.h"
+#include "udev-util.h"
+#include "virt.h"
/* use 8 MB for receive socket kernel queue. */
#define RCVBUF_SIZE (8*1024*1024)
@@ -277,6 +279,350 @@ static int manager_connect_udev(Manager *m) {
return 0;
}
+int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) {
+ Manager *m = userdata;
+ Link *link = NULL;
+ uint16_t type;
+ uint32_t ifindex, priority = 0;
+ unsigned char protocol, scope, tos, table;
+ int family;
+ unsigned char dst_prefixlen, src_prefixlen;
+ union in_addr_union dst = {}, gw = {}, src = {}, prefsrc = {};
+ Route *route = NULL;
+ int r;
+
+ assert(rtnl);
+ assert(message);
+ assert(m);
+
+ if (sd_netlink_message_is_error(message)) {
+ r = sd_netlink_message_get_errno(message);
+ if (r < 0)
+ log_warning_errno(r, "rtnl: failed to receive route: %m");
+
+ return 0;
+ }
+
+ r = sd_netlink_message_get_type(message, &type);
+ if (r < 0) {
+ log_warning_errno(r, "rtnl: could not get message type: %m");
+ return 0;
+ } else if (type != RTM_NEWROUTE && type != RTM_DELROUTE) {
+ log_warning("rtnl: received unexpected message type when processing route");
+ return 0;
+ }
+
+ r = sd_netlink_message_read_u32(message, RTA_OIF, &ifindex);
+ if (r == -ENODATA) {
+ log_debug("rtnl: received route without ifindex, ignoring");
+ return 0;
+ } else if (r < 0) {
+ log_warning_errno(r, "rtnl: could not get ifindex from route, ignoring: %m");
+ return 0;
+ } else if (ifindex <= 0) {
+ log_warning("rtnl: received route message with invalid ifindex, ignoring: %d", ifindex);
+ return 0;
+ } else {
+ r = link_get(m, ifindex, &link);
+ if (r < 0 || !link) {
+ /* when enumerating we might be out of sync, but we will
+ * get the route again, so just ignore it */
+ if (!m->enumerating)
+ log_warning("rtnl: received route for nonexistent link (%d), ignoring", ifindex);
+ return 0;
+ }
+ }
+
+ r = sd_rtnl_message_route_get_family(message, &family);
+ if (r < 0 || !IN_SET(family, AF_INET, AF_INET6)) {
+ log_link_warning(link, "rtnl: received address with invalid family, ignoring.");
+ return 0;
+ }
+
+ r = sd_rtnl_message_route_get_protocol(message, &protocol);
+ if (r < 0) {
+ log_warning_errno(r, "rtnl: could not get route protocol: %m");
+ return 0;
+ }
+
+ switch (family) {
+ case AF_INET:
+ r = sd_netlink_message_read_in_addr(message, RTA_DST, &dst.in);
+ if (r < 0 && r != -ENODATA) {
+ log_link_warning_errno(link, r, "rtnl: received route without valid destination, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_netlink_message_read_in_addr(message, RTA_GATEWAY, &gw.in);
+ if (r < 0 && r != -ENODATA) {
+ log_link_warning_errno(link, r, "rtnl: received route with invalid gateway, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_netlink_message_read_in_addr(message, RTA_SRC, &src.in);
+ if (r < 0 && r != -ENODATA) {
+ log_link_warning_errno(link, r, "rtnl: received route with invalid source, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_netlink_message_read_in_addr(message, RTA_PREFSRC, &prefsrc.in);
+ if (r < 0 && r != -ENODATA) {
+ log_link_warning_errno(link, r, "rtnl: received route with invalid preferred source, ignoring: %m");
+ return 0;
+ }
+
+ break;
+
+ case AF_INET6:
+ r = sd_netlink_message_read_in6_addr(message, RTA_DST, &dst.in6);
+ if (r < 0 && r != -ENODATA) {
+ log_link_warning_errno(link, r, "rtnl: received route without valid destination, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_netlink_message_read_in6_addr(message, RTA_GATEWAY, &gw.in6);
+ if (r < 0 && r != -ENODATA) {
+ log_link_warning_errno(link, r, "rtnl: received route with invalid gateway, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_netlink_message_read_in6_addr(message, RTA_SRC, &src.in6);
+ if (r < 0 && r != -ENODATA) {
+ log_link_warning_errno(link, r, "rtnl: received route with invalid source, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_netlink_message_read_in6_addr(message, RTA_PREFSRC, &prefsrc.in6);
+ if (r < 0 && r != -ENODATA) {
+ log_link_warning_errno(link, r, "rtnl: received route with invalid preferred source, ignoring: %m");
+ return 0;
+ }
+
+ break;
+
+ default:
+ log_link_debug(link, "rtnl: ignoring unsupported address family: %d", family);
+ return 0;
+ }
+
+ r = sd_rtnl_message_route_get_dst_prefixlen(message, &dst_prefixlen);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "rtnl: received route with invalid destination prefixlen, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_rtnl_message_route_get_src_prefixlen(message, &src_prefixlen);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "rtnl: received route with invalid source prefixlen, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_rtnl_message_route_get_scope(message, &scope);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "rtnl: received route with invalid scope, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_rtnl_message_route_get_tos(message, &tos);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "rtnl: received route with invalid tos, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_rtnl_message_route_get_table(message, &table);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "rtnl: received route with invalid table, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_netlink_message_read_u32(message, RTA_PRIORITY, &priority);
+ if (r < 0 && r != -ENODATA) {
+ log_link_warning_errno(link, r, "rtnl: received route with invalid priority, ignoring: %m");
+ return 0;
+ }
+
+ route_get(link, family, &dst, dst_prefixlen, tos, priority, table, &route);
+
+ switch (type) {
+ case RTM_NEWROUTE:
+ if (!route) {
+ /* A route appeared that we did not request */
+ r = route_add_foreign(link, family, &dst, dst_prefixlen, tos, priority, table, &route);
+ if (r < 0)
+ return 0;
+ }
+
+ route_update(route, &src, src_prefixlen, &gw, &prefsrc, scope, protocol);
+
+ break;
+
+ case RTM_DELROUTE:
+
+ if (route)
+ route_drop(route);
+
+ break;
+ default:
+ assert_not_reached("Received invalid RTNL message type");
+ }
+
+ return 1;
+}
+
+int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) {
+ Manager *m = userdata;
+ Link *link = NULL;
+ uint16_t type;
+ unsigned char flags;
+ int family;
+ unsigned char prefixlen;
+ unsigned char scope;
+ union in_addr_union in_addr;
+ struct ifa_cacheinfo cinfo;
+ Address *address = NULL;
+ char buf[INET6_ADDRSTRLEN], valid_buf[FORMAT_TIMESPAN_MAX];
+ const char *valid_str = NULL;
+ int r, ifindex;
+
+ assert(rtnl);
+ assert(message);
+ assert(m);
+
+ if (sd_netlink_message_is_error(message)) {
+ r = sd_netlink_message_get_errno(message);
+ if (r < 0)
+ log_warning_errno(r, "rtnl: failed to receive address: %m");
+
+ return 0;
+ }
+
+ r = sd_netlink_message_get_type(message, &type);
+ if (r < 0) {
+ 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 from address: %m");
+ return 0;
+ } else if (ifindex <= 0) {
+ log_warning("rtnl: received address message with invalid ifindex: %d", ifindex);
+ return 0;
+ } else {
+ r = link_get(m, ifindex, &link);
+ if (r < 0 || !link) {
+ /* when enumerating we might be out of sync, but we will
+ * get the address again, so just ignore it */
+ if (!m->enumerating)
+ log_warning("rtnl: received address for nonexistent link (%d), ignoring", ifindex);
+ return 0;
+ }
+ }
+
+ r = sd_rtnl_message_addr_get_family(message, &family);
+ if (r < 0 || !IN_SET(family, AF_INET, AF_INET6)) {
+ log_link_warning(link, "rtnl: received address with invalid family, ignoring.");
+ return 0;
+ }
+
+ r = sd_rtnl_message_addr_get_prefixlen(message, &prefixlen);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "rtnl: received address with invalid prefixlen, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_rtnl_message_addr_get_scope(message, &scope);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "rtnl: received address with invalid scope, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_rtnl_message_addr_get_flags(message, &flags);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "rtnl: received address with invalid flags, ignoring: %m");
+ return 0;
+ }
+
+ switch (family) {
+ case AF_INET:
+ r = sd_netlink_message_read_in_addr(message, IFA_LOCAL, &in_addr.in);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "rtnl: received address without valid address, ignoring: %m");
+ return 0;
+ }
+
+ break;
+
+ case AF_INET6:
+ r = sd_netlink_message_read_in6_addr(message, IFA_ADDRESS, &in_addr.in6);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "rtnl: received address without valid address, ignoring: %m");
+ return 0;
+ }
+
+ break;
+
+ default:
+ log_link_debug(link, "rtnl: ignoring unsupported address family: %d", family);
+ }
+
+ if (!inet_ntop(family, &in_addr, buf, INET6_ADDRSTRLEN)) {
+ log_link_warning(link, "Could not print address");
+ return 0;
+ }
+
+ r = sd_netlink_message_read_cache_info(message, IFA_CACHEINFO, &cinfo);
+ if (r >= 0) {
+ if (cinfo.ifa_valid != CACHE_INFO_INFINITY_LIFE_TIME)
+ valid_str = format_timespan(valid_buf, FORMAT_TIMESPAN_MAX,
+ cinfo.ifa_valid * USEC_PER_SEC,
+ USEC_PER_SEC);
+ }
+
+ address_get(link, family, &in_addr, prefixlen, &address);
+
+ switch (type) {
+ case RTM_NEWADDR:
+ if (address)
+ log_link_debug(link, "Updating address: %s/%u (valid %s%s)", buf, prefixlen,
+ valid_str ? "for " : "forever", valid_str ?: "");
+ else {
+ /* An address appeared that we did not request */
+ r = address_add_foreign(link, family, &in_addr, prefixlen, &address);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Failed to add address %s/%u: %m", buf, prefixlen);
+ return 0;
+ } else
+ log_link_debug(link, "Adding address: %s/%u (valid %s%s)", buf, prefixlen,
+ valid_str ? "for " : "forever", valid_str ?: "");
+ }
+
+ address_update(address, flags, scope, &cinfo);
+
+ break;
+
+ case RTM_DELADDR:
+
+ if (address) {
+ log_link_debug(link, "Removing address: %s/%u (valid %s%s)", buf, prefixlen,
+ valid_str ? "for " : "forever", valid_str ?: "");
+ address_drop(address);
+ } else
+ log_link_warning(link, "Removing non-existent address: %s/%u (valid %s%s)", buf, prefixlen,
+ valid_str ? "for " : "forever", valid_str ?: "");
+
+ break;
+ default:
+ assert_not_reached("Received invalid RTNL message type");
+ }
+
+ return 1;
+}
+
static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) {
Manager *m = userdata;
Link *link = NULL;
@@ -313,15 +659,16 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa
} else if (ifindex <= 0) {
log_warning("rtnl: received link message with invalid ifindex: %d", ifindex);
return 0;
- } else
- link_get(m, ifindex, &link);
+ }
r = sd_netlink_message_read_string(message, IFLA_IFNAME, &name);
if (r < 0) {
log_warning_errno(r, "rtnl: Received link message without ifname: %m");
return 0;
- } else
- netdev_get(m, name, &netdev);
+ }
+
+ (void) link_get(m, ifindex, &link);
+ (void) netdev_get(m, name, &netdev);
switch (type) {
case RTM_NEWLINK:
@@ -410,17 +757,232 @@ static int manager_connect_rtnl(Manager *m) {
if (r < 0)
return r;
- r = sd_netlink_add_match(m->rtnl, RTM_NEWADDR, &link_rtnl_process_address, m);
+ r = sd_netlink_add_match(m->rtnl, RTM_NEWADDR, &manager_rtnl_process_address, m);
if (r < 0)
return r;
- r = sd_netlink_add_match(m->rtnl, RTM_DELADDR, &link_rtnl_process_address, m);
+ r = sd_netlink_add_match(m->rtnl, RTM_DELADDR, &manager_rtnl_process_address, m);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_add_match(m->rtnl, RTM_NEWROUTE, &manager_rtnl_process_route, m);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_add_match(m->rtnl, RTM_DELROUTE, &manager_rtnl_process_route, m);
if (r < 0)
return r;
return 0;
}
+static int set_put_in_addr(Set *s, const struct in_addr *address) {
+ char *p;
+ int r;
+
+ assert(s);
+
+ r = in_addr_to_string(AF_INET, (const union in_addr_union*) address, &p);
+ if (r < 0)
+ return r;
+
+ r = set_consume(s, p);
+ if (r == -EEXIST)
+ return 0;
+
+ return r;
+}
+
+static int set_put_in_addrv(Set *s, const struct in_addr *addresses, int n) {
+ int r, i, c = 0;
+
+ assert(s);
+ assert(n <= 0 || addresses);
+
+ for (i = 0; i < n; i++) {
+ r = set_put_in_addr(s, addresses+i);
+ if (r < 0)
+ return r;
+
+ c += r;
+ }
+
+ return c;
+}
+
+static void print_string_set(FILE *f, const char *field, Set *s) {
+ bool space = false;
+ Iterator i;
+ char *p;
+
+ if (set_isempty(s))
+ return;
+
+ fputs(field, f);
+
+ SET_FOREACH(p, s, i) {
+ if (space)
+ fputc(' ', f);
+ fputs(p, f);
+ space = true;
+ }
+ fputc('\n', f);
+}
+
+static int manager_save(Manager *m) {
+ _cleanup_set_free_free_ Set *dns = NULL, *ntp = NULL, *domains = NULL;
+ Link *link;
+ Iterator i;
+ _cleanup_free_ char *temp_path = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ LinkOperationalState operstate = LINK_OPERSTATE_OFF;
+ const char *operstate_str;
+ int r;
+
+ assert(m);
+ assert(m->state_file);
+
+ /* We add all NTP and DNS server to a set, to filter out duplicates */
+ dns = set_new(&string_hash_ops);
+ if (!dns)
+ return -ENOMEM;
+
+ ntp = set_new(&string_hash_ops);
+ if (!ntp)
+ return -ENOMEM;
+
+ domains = set_new(&string_hash_ops);
+ if (!domains)
+ return -ENOMEM;
+
+ HASHMAP_FOREACH(link, m->links, i) {
+ if (link->flags & IFF_LOOPBACK)
+ continue;
+
+ if (link->operstate > operstate)
+ operstate = link->operstate;
+
+ if (!link->network)
+ continue;
+
+ /* First add the static configured entries */
+ r = set_put_strdupv(dns, link->network->dns);
+ if (r < 0)
+ return r;
+
+ r = set_put_strdupv(ntp, link->network->ntp);
+ if (r < 0)
+ return r;
+
+ r = set_put_strdupv(domains, link->network->domains);
+ if (r < 0)
+ return r;
+
+ if (!link->dhcp_lease)
+ continue;
+
+ /* Secondly, add the entries acquired via DHCP */
+ if (link->network->dhcp_dns) {
+ const struct in_addr *addresses;
+
+ r = sd_dhcp_lease_get_dns(link->dhcp_lease, &addresses);
+ if (r > 0) {
+ r = set_put_in_addrv(dns, addresses, r);
+ if (r < 0)
+ return r;
+ } else if (r < 0 && r != -ENODATA)
+ return r;
+ }
+
+ if (link->network->dhcp_ntp) {
+ const struct in_addr *addresses;
+
+ r = sd_dhcp_lease_get_ntp(link->dhcp_lease, &addresses);
+ if (r > 0) {
+ r = set_put_in_addrv(ntp, addresses, r);
+ if (r < 0)
+ return r;
+ } else if (r < 0 && r != -ENODATA)
+ return r;
+ }
+
+ if (link->network->dhcp_domains) {
+ const char *domainname;
+
+ r = sd_dhcp_lease_get_domainname(link->dhcp_lease, &domainname);
+ if (r >= 0) {
+ r = set_put_strdup(domains, domainname);
+ if (r < 0)
+ return r;
+ } else if (r != -ENODATA)
+ return r;
+ }
+ }
+
+ operstate_str = link_operstate_to_string(operstate);
+ assert(operstate_str);
+
+ r = fopen_temporary(m->state_file, &f, &temp_path);
+ if (r < 0)
+ return r;
+
+ fchmod(fileno(f), 0644);
+
+ fprintf(f,
+ "# This is private data. Do not parse.\n"
+ "OPER_STATE=%s\n", operstate_str);
+
+ print_string_set(f, "DNS=", dns);
+ print_string_set(f, "NTP=", ntp);
+ print_string_set(f, "DOMAINS=", domains);
+
+ r = fflush_and_check(f);
+ if (r < 0)
+ goto fail;
+
+ if (rename(temp_path, m->state_file) < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ if (m->operational_state != operstate) {
+ m->operational_state = operstate;
+ r = manager_send_changed(m, "OperationalState", NULL);
+ if (r < 0)
+ log_error_errno(r, "Could not emit changed OperationalState: %m");
+ }
+
+ m->dirty = false;
+
+ return 0;
+
+fail:
+ (void) unlink(m->state_file);
+ (void) unlink(temp_path);
+
+ return log_error_errno(r, "Failed to save network state to %s: %m", m->state_file);
+}
+
+static int manager_dirty_handler(sd_event_source *s, void *userdata) {
+ Manager *m = userdata;
+ Link *link;
+ Iterator i;
+ int r;
+
+ assert(m);
+
+ if (m->dirty)
+ manager_save(m);
+
+ SET_FOREACH(link, m->dirty_links, i) {
+ r = link_save(link);
+ if (r >= 0)
+ link_clean(link);
+ }
+
+ return 1;
+}
+
int manager_new(Manager **ret) {
_cleanup_manager_free_ Manager *m = NULL;
int r;
@@ -442,6 +1004,10 @@ int manager_new(Manager **ret) {
sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
+ r = sd_event_add_post(m->event, NULL, manager_dirty_handler, m);
+ if (r < 0)
+ return r;
+
r = manager_connect_rtnl(m);
if (r < 0)
return r;
@@ -477,14 +1043,6 @@ void manager_free(Manager *m) {
free(m->state_file);
- sd_event_source_unref(m->udev_event_source);
- udev_monitor_unref(m->udev_monitor);
- udev_unref(m->udev);
-
- sd_bus_unref(m->bus);
- sd_bus_slot_unref(m->prepare_for_sleep_slot);
- sd_event_source_unref(m->bus_retry_event_source);
-
while ((link = hashmap_first(m->links)))
link_unref(link);
hashmap_free(m->links);
@@ -504,6 +1062,14 @@ void manager_free(Manager *m) {
sd_netlink_unref(m->rtnl);
sd_event_unref(m->event);
+ sd_event_source_unref(m->udev_event_source);
+ udev_monitor_unref(m->udev_monitor);
+ udev_unref(m->udev);
+
+ sd_bus_unref(m->bus);
+ sd_bus_slot_unref(m->prepare_for_sleep_slot);
+ sd_event_source_unref(m->bus_retry_event_source);
+
free(m);
}
@@ -528,7 +1094,8 @@ static bool manager_check_idle(void *userdata) {
link_ipv4ll_enabled(link) ||
link_dhcp4_server_enabled(link) ||
link_dhcp4_enabled(link) ||
- link_dhcp6_enabled(link))
+ link_dhcp6_enabled(link) ||
+ link_ipv6_accept_ra_enabled(link))
return false;
}
@@ -536,8 +1103,19 @@ static bool manager_check_idle(void *userdata) {
}
int manager_run(Manager *m) {
+ Link *link;
+ Iterator i;
+
assert(m);
+ /* The dirty handler will deal with future serialization, but the first one
+ must be done explicitly. */
+
+ manager_save(m);
+
+ HASHMAP_FOREACH(link, m->links, i)
+ link_save(link);
+
if (m->bus)
return bus_event_loop_with_idle(
m->event,
@@ -574,7 +1152,7 @@ bool manager_should_reload(Manager *m) {
}
int manager_rtnl_enumerate_links(Manager *m) {
- _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
sd_netlink_message *link;
int r;
@@ -609,7 +1187,7 @@ int manager_rtnl_enumerate_links(Manager *m) {
}
int manager_rtnl_enumerate_addresses(Manager *m) {
- _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
sd_netlink_message *addr;
int r;
@@ -633,7 +1211,7 @@ int manager_rtnl_enumerate_addresses(Manager *m) {
m->enumerating = true;
- k = link_rtnl_process_address(m->rtnl, addr, m);
+ k = manager_rtnl_process_address(m->rtnl, addr, m);
if (k < 0)
r = k;
@@ -643,189 +1221,39 @@ int manager_rtnl_enumerate_addresses(Manager *m) {
return r;
}
-static int set_put_in_addr(Set *s, const struct in_addr *address) {
- char *p;
+int manager_rtnl_enumerate_routes(Manager *m) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
+ sd_netlink_message *route;
int r;
- assert(s);
+ assert(m);
+ assert(m->rtnl);
- r = in_addr_to_string(AF_INET, (const union in_addr_union*) address, &p);
+ r = sd_rtnl_message_new_route(m->rtnl, &req, RTM_GETROUTE, 0, 0);
if (r < 0)
return r;
- r = set_consume(s, p);
- if (r == -EEXIST)
- return 0;
-
- return r;
-}
-
-static int set_put_in_addrv(Set *s, const struct in_addr *addresses, int n) {
- int r, i, c = 0;
-
- assert(s);
- assert(n <= 0 || addresses);
-
- for (i = 0; i < n; i++) {
- r = set_put_in_addr(s, addresses+i);
- if (r < 0)
- return r;
-
- c += r;
- }
-
- return c;
-}
-
-static void print_string_set(FILE *f, const char *field, Set *s) {
- bool space = false;
- Iterator i;
- char *p;
-
- if (set_isempty(s))
- return;
-
- fputs(field, f);
-
- SET_FOREACH(p, s, i) {
- if (space)
- fputc(' ', f);
- fputs(p, f);
- space = true;
- }
- fputc('\n', f);
-}
-
-int manager_save(Manager *m) {
- _cleanup_set_free_free_ Set *dns = NULL, *ntp = NULL, *domains = NULL;
- Link *link;
- Iterator i;
- _cleanup_free_ char *temp_path = NULL;
- _cleanup_fclose_ FILE *f = NULL;
- LinkOperationalState operstate = LINK_OPERSTATE_OFF;
- const char *operstate_str;
- int r;
-
- assert(m);
- assert(m->state_file);
-
- /* We add all NTP and DNS server to a set, to filter out duplicates */
- dns = set_new(&string_hash_ops);
- if (!dns)
- return -ENOMEM;
-
- ntp = set_new(&string_hash_ops);
- if (!ntp)
- return -ENOMEM;
-
- domains = set_new(&string_hash_ops);
- if (!domains)
- return -ENOMEM;
-
- HASHMAP_FOREACH(link, m->links, i) {
- if (link->flags & IFF_LOOPBACK)
- continue;
-
- if (link->operstate > operstate)
- operstate = link->operstate;
-
- if (!link->network)
- continue;
-
- /* First add the static configured entries */
- r = set_put_strdupv(dns, link->network->dns);
- if (r < 0)
- return r;
-
- r = set_put_strdupv(ntp, link->network->ntp);
- if (r < 0)
- return r;
-
- r = set_put_strdupv(domains, link->network->domains);
- if (r < 0)
- return r;
-
- if (!link->dhcp_lease)
- continue;
-
- /* Secondly, add the entries acquired via DHCP */
- if (link->network->dhcp_dns) {
- const struct in_addr *addresses;
-
- r = sd_dhcp_lease_get_dns(link->dhcp_lease, &addresses);
- if (r > 0) {
- r = set_put_in_addrv(dns, addresses, r);
- if (r < 0)
- return r;
- } else if (r < 0 && r != -ENODATA)
- return r;
- }
-
- if (link->network->dhcp_ntp) {
- const struct in_addr *addresses;
-
- r = sd_dhcp_lease_get_ntp(link->dhcp_lease, &addresses);
- if (r > 0) {
- r = set_put_in_addrv(ntp, addresses, r);
- if (r < 0)
- return r;
- } else if (r < 0 && r != -ENODATA)
- return r;
- }
-
- if (link->network->dhcp_domains) {
- const char *domainname;
-
- r = sd_dhcp_lease_get_domainname(link->dhcp_lease, &domainname);
- if (r >= 0) {
- r = set_put_strdup(domains, domainname);
- if (r < 0)
- return r;
- } else if (r != -ENODATA)
- return r;
- }
- }
-
- operstate_str = link_operstate_to_string(operstate);
- assert(operstate_str);
-
- r = fopen_temporary(m->state_file, &f, &temp_path);
+ r = sd_netlink_message_request_dump(req, true);
if (r < 0)
return r;
- fchmod(fileno(f), 0644);
-
- fprintf(f,
- "# This is private data. Do not parse.\n"
- "OPER_STATE=%s\n", operstate_str);
-
- print_string_set(f, "DNS=", dns);
- print_string_set(f, "NTP=", ntp);
- print_string_set(f, "DOMAINS=", domains);
-
- r = fflush_and_check(f);
+ r = sd_netlink_call(m->rtnl, req, 0, &reply);
if (r < 0)
- goto fail;
+ return r;
- if (rename(temp_path, m->state_file) < 0) {
- r = -errno;
- goto fail;
- }
+ for (route = reply; route; route = sd_netlink_message_next(route)) {
+ int k;
- if (m->operational_state != operstate) {
- m->operational_state = operstate;
- r = manager_send_changed(m, "OperationalState", NULL);
- if (r < 0)
- log_error_errno(r, "Could not emit changed OperationalState: %m");
- }
+ m->enumerating = true;
- return 0;
+ k = manager_rtnl_process_route(m->rtnl, route, m);
+ if (k < 0)
+ r = k;
-fail:
- (void) unlink(m->state_file);
- (void) unlink(temp_path);
+ m->enumerating = false;
+ }
- return log_error_errno(r, "Failed to save network state to %s: %m", m->state_file);
+ return r;
}
int manager_address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found) {
@@ -884,3 +1312,10 @@ Link* manager_find_uplink(Manager *m, Link *exclude) {
return NULL;
}
+
+void manager_dirty(Manager *manager) {
+ assert(manager);
+
+ /* the serialized state in /run is no longer up-to-date */
+ manager->dirty = true;
+}
diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c
new file mode 100644
index 0000000000..483efd17c3
--- /dev/null
+++ b/src/network/networkd-ndisc.c
@@ -0,0 +1,251 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Intel Corporation. All rights reserved.
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General 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 <netinet/ether.h>
+#include <netinet/icmp6.h>
+#include <linux/if.h>
+
+#include "sd-ndisc.h"
+
+#include "networkd-link.h"
+
+static int ndisc_netlink_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
+ _cleanup_link_unref_ Link *link = userdata;
+ int r;
+
+ assert(link);
+ assert(link->ndisc_messages > 0);
+
+ link->ndisc_messages --;
+
+ r = sd_netlink_message_get_errno(m);
+ if (r < 0 && r != -EEXIST) {
+ log_link_error_errno(link, r, "Could not set NDisc route or address: %m");
+ link_enter_failed(link);
+ }
+
+ if (link->ndisc_messages == 0) {
+ link->ndisc_configured = true;
+ link_check_ready(link);
+ }
+
+ return 1;
+}
+
+static void ndisc_prefix_autonomous_handler(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen,
+ unsigned lifetime_preferred, unsigned lifetime_valid, void *userdata) {
+ _cleanup_address_free_ Address *address = NULL;
+ Link *link = userdata;
+ usec_t time_now;
+ int r;
+
+ assert(nd);
+ assert(link);
+ assert(link->network);
+
+ if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+ return;
+
+ r = address_new(&address);
+ if (r < 0) {
+ log_link_error_errno(link, r, "Could not allocate address: %m");
+ return;
+ }
+
+ assert_se(sd_event_now(link->manager->event, clock_boottime_or_monotonic(), &time_now) >= 0);
+
+ address->family = AF_INET6;
+ address->in_addr.in6 = *prefix;
+ if (in_addr_is_null(AF_INET6, (const union in_addr_union *) &link->network->ipv6_token) == 0)
+ memcpy(((char *)&address->in_addr.in6) + 8, ((char *)&link->network->ipv6_token) + 8, 8);
+ else {
+ /* see RFC4291 section 2.5.1 */
+ address->in_addr.in6.__in6_u.__u6_addr8[8] = link->mac.ether_addr_octet[0];
+ address->in_addr.in6.__in6_u.__u6_addr8[8] ^= 1 << 1;
+ address->in_addr.in6.__in6_u.__u6_addr8[9] = link->mac.ether_addr_octet[1];
+ address->in_addr.in6.__in6_u.__u6_addr8[10] = link->mac.ether_addr_octet[2];
+ address->in_addr.in6.__in6_u.__u6_addr8[11] = 0xff;
+ address->in_addr.in6.__in6_u.__u6_addr8[12] = 0xfe;
+ address->in_addr.in6.__in6_u.__u6_addr8[13] = link->mac.ether_addr_octet[3];
+ address->in_addr.in6.__in6_u.__u6_addr8[14] = link->mac.ether_addr_octet[4];
+ address->in_addr.in6.__in6_u.__u6_addr8[15] = link->mac.ether_addr_octet[5];
+ }
+ address->prefixlen = prefixlen;
+ address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR;
+ address->cinfo.ifa_prefered = lifetime_preferred;
+ address->cinfo.ifa_valid = lifetime_valid;
+
+ r = address_configure(address, link, ndisc_netlink_handler, true);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Could not set SLAAC address: %m");
+ link_enter_failed(link);
+ return;
+ }
+
+ link->ndisc_messages ++;
+}
+
+static void ndisc_prefix_onlink_handler(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen, unsigned lifetime, void *userdata) {
+ _cleanup_route_free_ Route *route = NULL;
+ Link *link = userdata;
+ usec_t time_now;
+ int r;
+
+ assert(nd);
+ assert(link);
+
+ if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+ return;
+
+ r = route_new(&route);
+ if (r < 0) {
+ log_link_error_errno(link, r, "Could not allocate route: %m");
+ return;
+ }
+
+ assert_se(sd_event_now(link->manager->event, clock_boottime_or_monotonic(), &time_now) >= 0);
+
+ route->family = AF_INET6;
+ route->table = RT_TABLE_MAIN;
+ route->protocol = RTPROT_RA;
+ route->flags = RTM_F_PREFIX;
+ route->dst.in6 = *prefix;
+ route->dst_prefixlen = prefixlen;
+ route->lifetime = time_now + lifetime * USEC_PER_SEC;
+
+ r = route_configure(route, link, ndisc_netlink_handler);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Could not set prefix route: %m");
+ link_enter_failed(link);
+ return;
+ }
+
+ link->ndisc_messages ++;
+}
+
+static void ndisc_router_handler(sd_ndisc *nd, uint8_t flags, const struct in6_addr *gateway, unsigned lifetime, int pref, void *userdata) {
+ _cleanup_route_free_ Route *route = NULL;
+ Link *link = userdata;
+ usec_t time_now;
+ int r;
+
+ assert(link);
+ assert(link->network);
+ assert(link->manager);
+
+ if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+ return;
+
+ if (flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER)) {
+ if (flags & ND_RA_FLAG_MANAGED)
+ dhcp6_request_address(link);
+
+ r = sd_dhcp6_client_start(link->dhcp6_client);
+ if (r < 0 && r != -EBUSY)
+ log_link_warning_errno(link, r, "Starting DHCPv6 client on NDisc request failed: %m");
+ }
+
+ if (!gateway)
+ return;
+
+ r = route_new(&route);
+ if (r < 0) {
+ log_link_error_errno(link, r, "Could not allocate route: %m");
+ return;
+ }
+
+ assert_se(sd_event_now(link->manager->event, clock_boottime_or_monotonic(), &time_now) >= 0);
+
+ route->family = AF_INET6;
+ route->table = RT_TABLE_MAIN;
+ route->protocol = RTPROT_RA;
+ route->pref = pref;
+ route->gw.in6 = *gateway;
+ route->lifetime = time_now + lifetime * USEC_PER_SEC;
+
+ r = route_configure(route, link, ndisc_netlink_handler);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Could not set default route: %m");
+ link_enter_failed(link);
+ return;
+ }
+
+ link->ndisc_messages ++;
+}
+
+static void ndisc_handler(sd_ndisc *nd, int event, void *userdata) {
+ Link *link = userdata;
+ int r;
+
+ assert(link);
+
+ if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+ return;
+
+ switch (event) {
+ case SD_NDISC_EVENT_TIMEOUT:
+ dhcp6_request_address(link);
+
+ r = sd_dhcp6_client_start(link->dhcp6_client);
+ if (r < 0 && r != -EBUSY)
+ log_link_warning_errno(link, r, "Starting DHCPv6 client after NDisc timeout failed: %m");
+
+ link->ndisc_configured = true;
+ link_check_ready(link);
+
+ break;
+ case SD_NDISC_EVENT_STOP:
+ break;
+ default:
+ log_link_warning(link, "IPv6 Neighbor Discovery unknown event: %d", event);
+ }
+}
+
+int ndisc_configure(Link *link) {
+ int r;
+
+ assert_return(link, -EINVAL);
+
+ r = sd_ndisc_new(&link->ndisc_router_discovery);
+ if (r < 0)
+ return r;
+
+ r = sd_ndisc_attach_event(link->ndisc_router_discovery, NULL, 0);
+ if (r < 0)
+ return r;
+
+ r = sd_ndisc_set_mac(link->ndisc_router_discovery, &link->mac);
+ if (r < 0)
+ return r;
+
+ r = sd_ndisc_set_index(link->ndisc_router_discovery, link->ifindex);
+ if (r < 0)
+ return r;
+
+ r = sd_ndisc_set_callback(link->ndisc_router_discovery,
+ ndisc_router_handler,
+ ndisc_prefix_onlink_handler,
+ ndisc_prefix_autonomous_handler,
+ ndisc_handler,
+ link);
+
+ return r;
+}
diff --git a/src/network/networkd-netdev-bond.c b/src/network/networkd-netdev-bond.c
index 12e2321674..50b9021d09 100644
--- a/src/network/networkd-netdev-bond.c
+++ b/src/network/networkd-netdev-bond.c
@@ -23,10 +23,14 @@
#include <netinet/ether.h>
#include <linux/if_bonding.h>
-#include "conf-parser.h"
#include "sd-netlink.h"
-#include "networkd-netdev-bond.h"
+
+#include "alloc-util.h"
+#include "conf-parser.h"
#include "missing.h"
+#include "networkd-netdev-bond.h"
+#include "string-table.h"
+#include "string-util.h"
/*
* Number of seconds between instances where the bonding
@@ -178,15 +182,18 @@ static uint8_t bond_xmit_hash_policy_to_kernel(BondXmitHashPolicy policy) {
}
static int netdev_bond_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
- Bond *b = BOND(netdev);
+ Bond *b;
ArpIpTarget *target = NULL;
int r, i = 0;
assert(netdev);
assert(!link);
- assert(b);
assert(m);
+ b = BOND(netdev);
+
+ assert(b);
+
if (b->mode != _NETDEV_BOND_MODE_INVALID) {
r = sd_netlink_message_append_u8(m, IFLA_BOND_MODE,
bond_mode_to_kernel(b->mode));
@@ -333,8 +340,6 @@ int config_parse_arp_ip_target_address(const char *unit,
void *data,
void *userdata) {
Bond *b = userdata;
- const char *word, *state;
- size_t l;
int r;
assert(filename);
@@ -342,14 +347,19 @@ int config_parse_arp_ip_target_address(const char *unit,
assert(rvalue);
assert(data);
- FOREACH_WORD_QUOTED(word, l, rvalue, state) {
+ for (;;) {
_cleanup_free_ ArpIpTarget *buffer = NULL;
_cleanup_free_ char *n = NULL;
int f;
- n = strndup(word, l);
- if (!n)
- return -ENOMEM;
+ r = extract_first_word(&rvalue, &n, NULL, 0);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse Bond ARP ip target address, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+
+ if (r == 0)
+ break;
buffer = new0(ArpIpTarget, 1);
if (!buffer)
@@ -357,12 +367,12 @@ int config_parse_arp_ip_target_address(const char *unit,
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);
+ log_syntax(unit, LOG_ERR, filename, line, r, "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);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Bond ARP ip target address is invalid, ignoring assignment: %s", n);
return 0;
}
@@ -373,16 +383,21 @@ int config_parse_arp_ip_target_address(const char *unit,
}
if (b->n_arp_ip_targets > NETDEV_BOND_ARP_TARGETS_MAX)
- log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "More than the maximum number of kernel-supported ARP ip targets specified: %d > %d", b->n_arp_ip_targets, NETDEV_BOND_ARP_TARGETS_MAX);
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "More than the maximum number of kernel-supported ARP ip targets specified: %d > %d",
+ b->n_arp_ip_targets, NETDEV_BOND_ARP_TARGETS_MAX);
return 0;
}
static void bond_done(NetDev *netdev) {
ArpIpTarget *t = NULL, *n = NULL;
- Bond *b = BOND(netdev);
+ Bond *b;
assert(netdev);
+
+ b = BOND(netdev);
+
assert(b);
LIST_FOREACH_SAFE(arp_ip_target, t, n, b->arp_ip_targets)
@@ -392,9 +407,12 @@ static void bond_done(NetDev *netdev) {
}
static void bond_init(NetDev *netdev) {
- Bond *b = BOND(netdev);
+ Bond *b;
assert(netdev);
+
+ b = BOND(netdev);
+
assert(b);
b->mode = _NETDEV_BOND_MODE_INVALID;
diff --git a/src/network/networkd-netdev-bridge.c b/src/network/networkd-netdev-bridge.c
index fd6af7e99b..e2a099c833 100644
--- a/src/network/networkd-netdev-bridge.c
+++ b/src/network/networkd-netdev-bridge.c
@@ -20,12 +20,97 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <net/if.h>
-#include "networkd-netdev-bridge.h"
#include "missing.h"
+#include "netlink-util.h"
+#include "networkd-netdev-bridge.h"
+
+/* callback for brige netdev's parameter set */
+static int netdev_bridge_set_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
+ _cleanup_netdev_unref_ NetDev *netdev = userdata;
+ int r;
+
+ assert(netdev);
+ assert(m);
+
+ r = sd_netlink_message_get_errno(m);
+ if (r < 0) {
+ log_netdev_warning_errno(netdev, r, "Bridge parameters could not be set: %m");
+ return 1;
+ }
+
+ log_netdev_debug(netdev, "Bridge parametres set success");
+
+ return 1;
+}
+
+static int netdev_bridge_post_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+ Bridge *b;
+ int r;
+
+ assert(netdev);
+
+ b = BRIDGE(netdev);
+
+ assert(b);
+
+ r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_NEWLINK, netdev->ifindex);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not allocate RTM_SETLINK message: %m");
+
+ r = sd_netlink_message_set_flags(req, NLM_F_REQUEST | NLM_F_ACK);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not set netlink flags: %m");
+
+ r = sd_netlink_message_open_container(req, IFLA_LINKINFO);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_PROTINFO attribute: %m");
+
+ r = sd_netlink_message_open_container_union(req, 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");
+
+ /* convert to jiffes */
+ if (b->forward_delay > 0) {
+ r = sd_netlink_message_append_u32(req, IFLA_BR_FORWARD_DELAY, usec_to_jiffies(b->forward_delay));
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_FORWARD_DELAY attribute: %m");
+ }
+
+ if (b->hello_time > 0) {
+ r = sd_netlink_message_append_u32(req, IFLA_BR_HELLO_TIME, usec_to_jiffies(b->hello_time));
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_HELLO_TIME attribute: %m");
+ }
+
+ if (b->max_age > 0) {
+ r = sd_netlink_message_append_u32(req, IFLA_BR_MAX_AGE, usec_to_jiffies(b->max_age));
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_MAX_AGE attribute: %m");
+ }
+
+ r = sd_netlink_message_close_container(req);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m");
+
+ r = sd_netlink_message_close_container(req);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_DATA attribute: %m");
+
+ r = sd_netlink_call_async(netdev->manager->rtnl, req, netdev_bridge_set_handler, netdev, 0, NULL);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m");
+
+ netdev_ref(netdev);
+
+ return r;
+}
const NetDevVTable bridge_vtable = {
.object_size = sizeof(Bridge),
- .sections = "Match\0NetDev\0",
+ .sections = "Match\0NetDev\0Bridge\0",
+ .post_create = netdev_bridge_post_create,
.create_type = NETDEV_CREATE_MASTER,
};
diff --git a/src/network/networkd-netdev-bridge.h b/src/network/networkd-netdev-bridge.h
index a7d02b1c91..d3bd15e0d6 100644
--- a/src/network/networkd-netdev-bridge.h
+++ b/src/network/networkd-netdev-bridge.h
@@ -27,6 +27,10 @@ typedef struct Bridge Bridge;
struct Bridge {
NetDev meta;
+
+ usec_t forward_delay;
+ usec_t hello_time;
+ usec_t max_age;
};
extern const NetDevVTable bridge_vtable;
diff --git a/src/network/networkd-netdev-gperf.gperf b/src/network/networkd-netdev-gperf.gperf
index e0bd0e024a..4a4b400e41 100644
--- a/src/network/networkd-netdev-gperf.gperf
+++ b/src/network/networkd-netdev-gperf.gperf
@@ -56,6 +56,7 @@ VXLAN.UDP6ZeroCheckSumRx, config_parse_bool, 0,
VXLAN.UDP6ZeroCheckSumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx)
VXLAN.FDBAgeingSec, config_parse_sec, 0, offsetof(VxLan, fdb_ageing)
VXLAN.GroupPolicyExtension, config_parse_bool, 0, offsetof(VxLan, group_policy)
+VXLAN.MaximumFDBEntries, config_parse_unsigned, 0, offsetof(VxLan, max_fdb)
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)
@@ -86,3 +87,6 @@ Bond.UpDelaySec, config_parse_sec, 0,
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)
+Bridge.HelloTimeSec, config_parse_sec, 0, offsetof(Bridge, hello_time)
+Bridge.MaxAgeSec, config_parse_sec, 0, offsetof(Bridge, max_age)
+Bridge.ForwardDelaySec, config_parse_sec, 0, offsetof(Bridge, forward_delay)
diff --git a/src/network/networkd-netdev-ipvlan.c b/src/network/networkd-netdev-ipvlan.c
index 5eb4a1eb36..27cb7d1bf0 100644
--- a/src/network/networkd-netdev-ipvlan.c
+++ b/src/network/networkd-netdev-ipvlan.c
@@ -21,8 +21,9 @@
#include <net/if.h>
-#include "networkd-netdev-ipvlan.h"
#include "conf-parser.h"
+#include "networkd-netdev-ipvlan.h"
+#include "string-table.h"
static const char* const ipvlan_mode_table[_NETDEV_IPVLAN_MODE_MAX] = {
[NETDEV_IPVLAN_MODE_L2] = "L2",
@@ -33,14 +34,17 @@ DEFINE_STRING_TABLE_LOOKUP(ipvlan_mode, IPVlanMode);
DEFINE_CONFIG_PARSE_ENUM(config_parse_ipvlan_mode, ipvlan_mode, IPVlanMode, "Failed to parse ipvlan mode");
static int netdev_ipvlan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *req) {
- IPVlan *m = IPVLAN(netdev);
+ IPVlan *m;
int r;
assert(netdev);
- assert(m);
assert(link);
assert(netdev->ifname);
+ m = IPVLAN(netdev);
+
+ assert(m);
+
if (m->mode != _NETDEV_IPVLAN_MODE_INVALID) {
r = sd_netlink_message_append_u16(req, IFLA_IPVLAN_MODE, m->mode);
if (r < 0)
@@ -51,9 +55,12 @@ static int netdev_ipvlan_fill_message_create(NetDev *netdev, Link *link, sd_netl
}
static void ipvlan_init(NetDev *n) {
- IPVlan *m = IPVLAN(n);
+ IPVlan *m;
assert(n);
+
+ m = IPVLAN(n);
+
assert(m);
m->mode = _NETDEV_IPVLAN_MODE_INVALID;
diff --git a/src/network/networkd-netdev-macvlan.c b/src/network/networkd-netdev-macvlan.c
index e17de793ce..7144823b2d 100644
--- a/src/network/networkd-netdev-macvlan.c
+++ b/src/network/networkd-netdev-macvlan.c
@@ -21,8 +21,9 @@
#include <net/if.h>
-#include "networkd-netdev-macvlan.h"
#include "conf-parser.h"
+#include "networkd-netdev-macvlan.h"
+#include "string-table.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 a906e473b6..a2c00161d3 100644
--- a/src/network/networkd-netdev-tunnel.c
+++ b/src/network/networkd-netdev-tunnel.c
@@ -26,11 +26,15 @@
#include <linux/ip6_tunnel.h>
#include "sd-netlink.h"
-#include "networkd-netdev-tunnel.h"
+
+#include "conf-parser.h"
+#include "missing.h"
#include "networkd-link.h"
+#include "networkd-netdev-tunnel.h"
+#include "parse-util.h"
+#include "string-table.h"
+#include "string-util.h"
#include "util.h"
-#include "missing.h"
-#include "conf-parser.h"
#define DEFAULT_TNL_HOP_LIMIT 64
#define IP6_FLOWINFO_FLOWLABEL htonl(0x000FFFFF)
@@ -52,7 +56,7 @@ static int netdev_ipip_fill_message_create(NetDev *netdev, Link *link, sd_netlin
assert(link);
assert(m);
assert(t);
- assert(t->family == AF_INET);
+ assert(t->family == AF_INET || t->family != -1);
r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex);
if (r < 0)
@@ -85,7 +89,7 @@ static int netdev_sit_fill_message_create(NetDev *netdev, Link *link, sd_netlink
assert(link);
assert(m);
assert(t);
- assert(t->family == AF_INET);
+ assert(t->family == AF_INET || t->family != -1);
r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex);
if (r < 0)
@@ -122,7 +126,7 @@ static int netdev_gre_fill_message_create(NetDev *netdev, Link *link, sd_netlink
t = GRETAP(netdev);
assert(t);
- assert(t->family == AF_INET);
+ assert(t->family == AF_INET || t->family != -1);
assert(link);
assert(m);
@@ -354,12 +358,7 @@ 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;
- }
-
- if (t->family != AF_INET && t->family != AF_INET6) {
+ if (t->family != AF_INET && t->family != AF_INET6 && t->family != 0) {
log_warning("Tunnel with invalid address family configured in %s. Ignoring", filename);
return -EINVAL;
}
@@ -393,15 +392,21 @@ int config_parse_tunnel_address(const char *unit,
assert(rvalue);
assert(data);
- r = in_addr_from_string_auto(rvalue, &f, &buffer);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Tunnel address is invalid, ignoring assignment: %s", rvalue);
+ if (streq(rvalue, "any")) {
+ t->family = 0;
return 0;
- }
+ } else {
- if (t->family != AF_UNSPEC && t->family != f) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Tunnel addresses incompatible, ignoring assignment: %s", rvalue);
- return 0;
+ r = in_addr_from_string_auto(rvalue, &f, &buffer);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Tunnel address is invalid, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+
+ if (t->family != AF_UNSPEC && t->family != f) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Tunnel addresses incompatible, ignoring assignment: %s", rvalue);
+ return 0;
+ }
}
t->family = f;
@@ -435,13 +440,14 @@ int config_parse_ipv6_flowlabel(const char* unit,
t->flags |= IP6_TNL_F_USE_ORIG_FLOWLABEL;
} else {
r = config_parse_int(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &k, userdata);
- if (r >= 0) {
- if (k > 0xFFFFF)
- log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse IPv6 flowlabel option, ignoring: %s", rvalue);
- else {
- *ipv6_flowlabel = htonl(k) & IP6_FLOWINFO_FLOWLABEL;
- t->flags &= ~IP6_TNL_F_USE_ORIG_FLOWLABEL;
- }
+ if (r < 0)
+ return r;
+
+ if (k > 0xFFFFF)
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IPv6 flowlabel option, ignoring: %s", rvalue);
+ else {
+ *ipv6_flowlabel = htonl(k) & IP6_FLOWINFO_FLOWLABEL;
+ t->flags &= ~IP6_TNL_F_USE_ORIG_FLOWLABEL;
}
}
@@ -471,13 +477,12 @@ int config_parse_encap_limit(const char* unit,
else {
r = safe_atoi(rvalue, &k);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r,
- "Failed to parse Tunnel Encapsulation Limit option, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse Tunnel Encapsulation Limit option, ignoring: %s", rvalue);
return 0;
}
if (k > 255 || k < 0)
- log_syntax(unit, LOG_ERR, filename, line, k, "Invalid Tunnel Encapsulation value, ignoring: %d", k);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid Tunnel Encapsulation value, ignoring: %d", k);
else {
t->encap_limit = k;
t->flags &= ~IP6_TNL_F_IGN_ENCAP_LIMIT;
@@ -494,6 +499,7 @@ static void ipip_init(NetDev *n) {
assert(t);
t->pmtudisc = true;
+ t->family = -1;
}
static void sit_init(NetDev *n) {
@@ -503,6 +509,7 @@ static void sit_init(NetDev *n) {
assert(t);
t->pmtudisc = true;
+ t->family = -1;
}
static void vti_init(NetDev *n) {
@@ -533,6 +540,7 @@ static void gre_init(NetDev *n) {
assert(t);
t->pmtudisc = true;
+ t->family = -1;
}
static void ip6gre_init(NetDev *n) {
diff --git a/src/network/networkd-netdev-tuntap.c b/src/network/networkd-netdev-tuntap.c
index 6a808b6205..3d504a8564 100644
--- a/src/network/networkd-netdev-tuntap.c
+++ b/src/network/networkd-netdev-tuntap.c
@@ -19,11 +19,14 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/ioctl.h>
#include <net/if.h>
+#include <sys/ioctl.h>
#include <linux/if_tun.h>
+#include "alloc-util.h"
+#include "fd-util.h"
#include "networkd-netdev-tuntap.h"
+#include "user-util.h"
#define TUN_DEV "/dev/net/tun"
diff --git a/src/network/networkd-netdev-veth.c b/src/network/networkd-netdev-veth.c
index e20f9f74e2..773a1ee6d1 100644
--- a/src/network/networkd-netdev-veth.c
+++ b/src/network/networkd-netdev-veth.c
@@ -23,17 +23,21 @@
#include <linux/veth.h>
#include "sd-netlink.h"
+
#include "networkd-netdev-veth.h"
static int netdev_veth_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
- Veth *v = VETH(netdev);
+ Veth *v;
int r;
assert(netdev);
assert(!link);
- assert(v);
assert(m);
+ v = VETH(netdev);
+
+ assert(v);
+
r = sd_netlink_message_open_container(m, VETH_INFO_PEER);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append VETH_INFO_PEER attribute: %m");
@@ -58,13 +62,16 @@ static int netdev_veth_fill_message_create(NetDev *netdev, Link *link, sd_netlin
}
static int netdev_veth_verify(NetDev *netdev, const char *filename) {
- Veth *v = VETH(netdev);
+ Veth *v;
int r;
assert(netdev);
- assert(v);
assert(filename);
+ v = VETH(netdev);
+
+ assert(v);
+
if (!v->ifname_peer) {
log_warning("Veth NetDev without peer name configured in %s. Ignoring",
filename);
@@ -84,9 +91,12 @@ static int netdev_veth_verify(NetDev *netdev, const char *filename) {
}
static void veth_done(NetDev *n) {
- Veth *v = VETH(n);
+ Veth *v;
assert(n);
+
+ v = VETH(n);
+
assert(v);
free(v->ifname_peer);
diff --git a/src/network/networkd-netdev-vlan.c b/src/network/networkd-netdev-vlan.c
index 195d1a944e..75fbdd355e 100644
--- a/src/network/networkd-netdev-vlan.c
+++ b/src/network/networkd-netdev-vlan.c
@@ -24,14 +24,17 @@
#include "networkd-netdev-vlan.h"
static int netdev_vlan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *req) {
- VLan *v = VLAN(netdev);
+ VLan *v;
int r;
assert(netdev);
- assert(v);
assert(link);
assert(req);
+ v = VLAN(netdev);
+
+ assert(v);
+
if (v->id <= VLANID_MAX) {
r = sd_netlink_message_append_u16(req, IFLA_VLAN_ID, v->id);
if (r < 0)
@@ -42,12 +45,15 @@ static int netdev_vlan_fill_message_create(NetDev *netdev, Link *link, sd_netlin
}
static int netdev_vlan_verify(NetDev *netdev, const char *filename) {
- VLan *v = VLAN(netdev);
+ VLan *v;
assert(netdev);
- assert(v);
assert(filename);
+ v = VLAN(netdev);
+
+ assert(v);
+
if (v->id > VLANID_MAX) {
log_warning("VLAN without valid Id (%"PRIu64") configured in %s. Ignoring", v->id, filename);
return -EINVAL;
diff --git a/src/network/networkd-netdev-vxlan.c b/src/network/networkd-netdev-vxlan.c
index 2518e2732b..7932b93335 100644
--- a/src/network/networkd-netdev-vxlan.c
+++ b/src/network/networkd-netdev-vxlan.c
@@ -22,20 +22,23 @@
#include <net/if.h>
#include "sd-netlink.h"
-#include "networkd-netdev-vxlan.h"
-#include "networkd-link.h"
+
#include "conf-parser.h"
#include "missing.h"
+#include "networkd-link.h"
+#include "networkd-netdev-vxlan.h"
static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
- VxLan *v = VXLAN(netdev);
+ VxLan *v;
int r;
assert(netdev);
- assert(v);
assert(link);
assert(m);
+ v = VXLAN(netdev);
+
+ assert(v);
if (v->id <= VXLAN_VID_MAX) {
r = sd_netlink_message_append_u32(m, IFLA_VXLAN_ID, v->id);
@@ -89,6 +92,12 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli
return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_AGEING attribute: %m");
}
+ if (v->max_fdb) {
+ r = sd_netlink_message_append_u32(m, IFLA_VXLAN_LIMIT, v->max_fdb);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_LIMIT attribute: %m");
+ }
+
r = sd_netlink_message_append_u8(m, IFLA_VXLAN_UDP_CSUM, v->udpcsum);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_UDP_CSUM attribute: %m");
@@ -131,14 +140,12 @@ int config_parse_vxlan_group_address(const char *unit,
r = in_addr_from_string_auto(rvalue, &f, &buffer);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "vxlan multicast group address is invalid, ignoring assignment: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "vxlan multicast group address is invalid, ignoring assignment: %s", rvalue);
return 0;
}
- if(v->family != AF_UNSPEC && v->family != f) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "vxlan multicast group incompatible, ignoring assignment: %s", rvalue);
+ if (v->family != AF_UNSPEC && v->family != f) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "vxlan multicast group incompatible, ignoring assignment: %s", rvalue);
return 0;
}
@@ -164,9 +171,12 @@ static int netdev_vxlan_verify(NetDev *netdev, const char *filename) {
}
static void vxlan_init(NetDev *netdev) {
- VxLan *v = VXLAN(netdev);
+ VxLan *v;
assert(netdev);
+
+ v = VXLAN(netdev);
+
assert(v);
v->id = VXLAN_VID_MAX + 1;
diff --git a/src/network/networkd-netdev-vxlan.h b/src/network/networkd-netdev-vxlan.h
index 4ec33946cc..16977ea6a9 100644
--- a/src/network/networkd-netdev-vxlan.h
+++ b/src/network/networkd-netdev-vxlan.h
@@ -23,9 +23,8 @@
typedef struct VxLan VxLan;
-#include "networkd-netdev.h"
-
#include "in-addr-util.h"
+#include "networkd-netdev.h"
#define VXLAN_VID_MAX (1u << 24) - 1
@@ -39,6 +38,7 @@ struct VxLan {
unsigned tos;
unsigned ttl;
+ unsigned max_fdb;
usec_t fdb_ageing;
diff --git a/src/network/networkd-netdev.c b/src/network/networkd-netdev.c
index ff1edf2c39..795f24ae4f 100644
--- a/src/network/networkd-netdev.c
+++ b/src/network/networkd-netdev.c
@@ -21,15 +21,19 @@
#include <net/if.h>
+#include "alloc-util.h"
#include "conf-files.h"
#include "conf-parser.h"
+#include "fd-util.h"
#include "list.h"
-#include "siphash24.h"
#include "netlink-util.h"
#include "network-internal.h"
-
-#include "networkd.h"
#include "networkd-netdev.h"
+#include "networkd.h"
+#include "siphash24.h"
+#include "stat-util.h"
+#include "string-table.h"
+#include "string-util.h"
const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
@@ -82,7 +86,7 @@ DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
DEFINE_CONFIG_PARSE_ENUM(config_parse_netdev_kind, netdev_kind, NetDevKind, "Failed to parse netdev kind");
static void netdev_cancel_callbacks(NetDev *netdev) {
- _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
netdev_join_callback *callback;
if (!netdev)
@@ -189,7 +193,7 @@ static int netdev_enter_failed(NetDev *netdev) {
}
static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_netlink_message_handler_t callback) {
- _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
int r;
assert(netdev);
@@ -245,6 +249,9 @@ static int netdev_enter_ready(NetDev *netdev) {
free(callback);
}
+ if (NETDEV_VTABLE(netdev)->post_create)
+ NETDEV_VTABLE(netdev)->post_create(netdev, NULL, NULL);
+
return 0;
}
@@ -283,7 +290,7 @@ int netdev_enslave(NetDev *netdev, Link *link, sd_netlink_message_handler_t call
if (r < 0)
return r;
} else if (IN_SET(netdev->state, NETDEV_STATE_LINGER, NETDEV_STATE_FAILED)) {
- _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
r = rtnl_message_new_synthetic_error(-ENODEV, 0, &m);
if (r >= 0)
@@ -404,7 +411,7 @@ int netdev_set_ifindex(NetDev *netdev, sd_netlink_message *message) {
int netdev_get_mac(const char *ifname, struct ether_addr **ret) {
_cleanup_free_ struct ether_addr *mac = NULL;
- uint8_t result[8];
+ uint64_t result;
size_t l, sz;
uint8_t *v;
int r;
@@ -431,10 +438,10 @@ int netdev_get_mac(const char *ifname, struct ether_addr **ret) {
/* Let's hash the host machine ID plus the container name. We
* use a fixed, but originally randomly created hash key here. */
- siphash24(result, v, sz, HASH_KEY.bytes);
+ result = siphash24(v, sz, HASH_KEY.bytes);
assert_cc(ETH_ALEN <= sizeof(result));
- memcpy(mac->ether_addr_octet, result, ETH_ALEN);
+ memcpy(mac->ether_addr_octet, &result, ETH_ALEN);
/* see eth_random_addr in the kernel */
mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
@@ -463,7 +470,7 @@ static int netdev_create(NetDev *netdev, Link *link,
log_netdev_debug(netdev, "Created");
} else {
- _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
r = sd_rtnl_message_new_link(netdev->manager->rtnl, &m, RTM_NEWLINK, 0);
if (r < 0)
diff --git a/src/network/networkd-netdev.h b/src/network/networkd-netdev.h
index 1f8510c4f7..3ab39efd57 100644
--- a/src/network/networkd-netdev.h
+++ b/src/network/networkd-netdev.h
@@ -26,8 +26,8 @@
typedef struct NetDev NetDev;
typedef struct NetDevVTable NetDevVTable;
-#include "networkd.h"
#include "networkd-link.h"
+#include "networkd.h"
typedef struct netdev_join_callback netdev_join_callback;
@@ -103,16 +103,16 @@ struct NetDev {
LIST_HEAD(netdev_join_callback, callbacks);
};
-#include "networkd-netdev-bridge.h"
#include "networkd-netdev-bond.h"
-#include "networkd-netdev-vlan.h"
-#include "networkd-netdev-macvlan.h"
+#include "networkd-netdev-bridge.h"
+#include "networkd-netdev-dummy.h"
#include "networkd-netdev-ipvlan.h"
-#include "networkd-netdev-vxlan.h"
-#include "networkd-netdev-veth.h"
+#include "networkd-netdev-macvlan.h"
#include "networkd-netdev-tunnel.h"
-#include "networkd-netdev-dummy.h"
#include "networkd-netdev-tuntap.h"
+#include "networkd-netdev-veth.h"
+#include "networkd-netdev-vlan.h"
+#include "networkd-netdev-vxlan.h"
struct NetDevVTable {
/* How much memory does an object of this unit type need */
@@ -141,6 +141,9 @@ struct NetDevVTable {
/* create netdev, if not done via rtnl */
int (*create)(NetDev *netdev);
+ /* perform additional configuration after netdev has been createad */
+ int (*post_create)(NetDev *netdev, Link *link, sd_netlink_message *message);
+
/* verify that compulsory configuration options were specified */
int (*config_verify)(NetDev *netdev, const char *filename);
};
diff --git a/src/network/networkd-network-bus.c b/src/network/networkd-network-bus.c
index 5717a15327..120760a986 100644
--- a/src/network/networkd-network-bus.c
+++ b/src/network/networkd-network-bus.c
@@ -19,9 +19,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "strv.h"
-
+#include "alloc-util.h"
#include "networkd.h"
+#include "string-util.h"
+#include "strv.h"
static int property_get_ether_addrs(
sd_bus *bus,
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 8257ab45da..2f2a36ccca 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -45,12 +45,17 @@ Network.Address, config_parse_address,
Network.Gateway, config_parse_gateway, 0, 0
Network.Domains, config_parse_domains, 0, offsetof(Network, domains)
Network.DNS, config_parse_strv, 0, offsetof(Network, dns)
-Network.LLMNR, config_parse_resolve, 0, offsetof(Network, llmnr)
+Network.LLMNR, config_parse_resolve_support, 0, offsetof(Network, llmnr)
+Network.MulticastDNS, config_parse_resolve_support, 0, offsetof(Network, mdns)
+Network.DNSSEC, config_parse_dnssec_mode, 0, offsetof(Network, dnssec_mode)
+Network.DNSSECNegativeTrustAnchors, config_parse_dnssec_negative_trust_anchors, 0, offsetof(Network, dnssec_negative_trust_anchors)
Network.NTP, config_parse_strv, 0, offsetof(Network, ntp)
Network.IPForward, config_parse_address_family_boolean_with_kernel,0, offsetof(Network, ip_forward)
Network.IPMasquerade, config_parse_bool, 0, offsetof(Network, ip_masquerade)
Network.IPv6PrivacyExtensions, config_parse_ipv6_privacy_extensions, 0, offsetof(Network, ipv6_privacy_extensions)
Network.IPv6AcceptRouterAdvertisements, config_parse_tristate, 0, offsetof(Network, ipv6_accept_ra)
+Network.IPv6DuplicateAddressDetection, config_parse_int, 0, offsetof(Network, ipv6_dad_transmits)
+Network.IPv6HopLimit, config_parse_int, 0, offsetof(Network, ipv6_hop_limit)
Network.BindCarrier, config_parse_strv, 0, offsetof(Network, bind_carrier)
Address.Address, config_parse_address, 0, 0
Address.Peer, config_parse_address, 0, 0
@@ -61,6 +66,7 @@ Route.Destination, config_parse_destination,
Route.Source, config_parse_destination, 0, 0
Route.Metric, config_parse_route_priority, 0, 0
Route.Scope, config_parse_route_scope, 0, 0
+Route.PreferredSource, config_parse_preferred_src, 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)
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 57495b58e0..c11cb3dcb3 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -22,15 +22,21 @@
#include <ctype.h>
#include <net/if.h>
+#include "alloc-util.h"
#include "conf-files.h"
#include "conf-parser.h"
-#include "util.h"
-#include "hostname-util.h"
#include "dns-domain.h"
+#include "fd-util.h"
+#include "hostname-util.h"
#include "network-internal.h"
-
-#include "networkd.h"
#include "networkd-network.h"
+#include "networkd.h"
+#include "parse-util.h"
+#include "set.h"
+#include "stat-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "util.h"
static int network_load_one(Manager *manager, const char *filename) {
_cleanup_network_free_ Network *network = NULL;
@@ -116,11 +122,15 @@ static int network_load_one(Manager *manager, const char *filename) {
network->unicast_flood = true;
network->llmnr = RESOLVE_SUPPORT_YES;
+ network->mdns = RESOLVE_SUPPORT_NO;
+ network->dnssec_mode = _DNSSEC_MODE_INVALID;
network->link_local = ADDRESS_FAMILY_IPV6;
network->ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO;
network->ipv6_accept_ra = -1;
+ network->ipv6_dad_transmits = -1;
+ network->ipv6_hop_limit = -1;
r = config_parse(NULL, filename, file,
"Match\0"
@@ -268,6 +278,8 @@ void network_free(Network *network) {
free(network->dhcp_server_dns);
free(network->dhcp_server_ntp);
+ set_free_free(network->dnssec_negative_trust_anchors);
+
free(network);
}
@@ -326,12 +338,12 @@ int network_get(Manager *manager, struct udev_device *device,
(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",
- IFNAMSIZ, ifname, network->filename);
+ log_warning("%s: found matching network '%s', based on potentially unpredictable ifname",
+ ifname, network->filename);
else
- log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, network->filename);
+ log_debug("%s: found matching network '%s'", ifname, network->filename);
} else
- log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, network->filename);
+ log_debug("%s: found matching network '%s'", ifname, network->filename);
*ret = network;
return 0;
@@ -346,6 +358,10 @@ int network_get(Manager *manager, struct udev_device *device,
int network_apply(Manager *manager, Network *network, Link *link) {
int r;
+ assert(manager);
+ assert(network);
+ assert(link);
+
link->network = network;
if (network->ipv4ll_route) {
@@ -355,7 +371,7 @@ int network_apply(Manager *manager, Network *network, Link *link) {
if (r < 0)
return r;
- r = inet_pton(AF_INET, "169.254.0.0", &route->dst_addr.in);
+ r = inet_pton(AF_INET, "169.254.0.0", &route->dst.in);
if (r == 0)
return -EINVAL;
if (r < 0)
@@ -364,14 +380,13 @@ int network_apply(Manager *manager, Network *network, Link *link) {
route->family = AF_INET;
route->dst_prefixlen = 16;
route->scope = RT_SCOPE_LINK;
- route->metrics = IPV4LL_ROUTE_METRIC;
+ route->priority = IPV4LL_ROUTE_METRIC;
route->protocol = RTPROT_STATIC;
}
- if (network->dns || network->ntp) {
- r = link_save(link);
- if (r < 0)
- return r;
+ if (network->dns || network->ntp || network->domains) {
+ manager_dirty(manager);
+ link_dirty(link);
}
return 0;
@@ -409,21 +424,18 @@ int config_parse_netdev(const char *unit,
kind = netdev_kind_from_string(kind_string);
if (kind == _NETDEV_KIND_INVALID) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Invalid NetDev kind: %s", lvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid NetDev kind: %s", lvalue);
return 0;
}
r = netdev_get(network->manager, rvalue, &netdev);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
return 0;
}
if (netdev->kind != kind) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
return 0;
}
@@ -443,9 +455,7 @@ int config_parse_netdev(const char *unit,
case NETDEV_KIND_VXLAN:
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: %m",
- rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Can not add VLAN '%s' to network: %m", rvalue);
return 0;
}
@@ -484,7 +494,7 @@ int config_parse_domains(const char *unit,
STRV_FOREACH(domain, *domains) {
if (is_localhost(*domain))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "'localhost' domain names may not be configured, ignoring assignment: %s", *domain);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "'localhost' domain names may not be configured, ignoring assignment: %s", *domain);
else {
r = dns_name_is_valid(*domain);
if (r <= 0 && !streq(*domain, "*")) {
@@ -540,7 +550,7 @@ int config_parse_tunnel(const char *unit,
netdev->kind != NETDEV_KIND_VTI6 &&
netdev->kind != NETDEV_KIND_IP6TNL
) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ log_syntax(unit, LOG_ERR, filename, line, 0,
"NetDev is not a tunnel, ignoring assignment: %s", rvalue);
return 0;
}
@@ -625,7 +635,7 @@ int config_parse_dhcp(
else if (streq(rvalue, "both"))
s = ADDRESS_FAMILY_YES;
else {
- log_syntax(unit, LOG_ERR, filename, line, s, "Failed to parse DHCP option, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse DHCP option, ignoring: %s", rvalue);
return 0;
}
}
@@ -670,13 +680,13 @@ int config_parse_ipv6token(
}
r = in_addr_is_null(AF_INET6, &buffer);
- if (r < 0) {
+ if (r != 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "IPv6 token can not be the ANY address, ignoring: %s", rvalue);
return 0;
}
if ((buffer.in6.s6_addr32[0] | buffer.in6.s6_addr32[1]) != 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "IPv6 token can not be longer than 64 bits, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "IPv6 token can not be longer than 64 bits, ignoring: %s", rvalue);
return 0;
}
@@ -730,7 +740,7 @@ int config_parse_ipv6_privacy_extensions(
if (streq(rvalue, "kernel"))
s = _IPV6_PRIVACY_EXTENSIONS_INVALID;
else {
- log_syntax(unit, LOG_ERR, filename, line, s, "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue);
return 0;
}
}
@@ -765,7 +775,7 @@ int config_parse_hostname(
return r;
if (!hostname_is_valid(hn, false)) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Hostname is not valid, ignoring assignment: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Hostname is not valid, ignoring assignment: %s", rvalue);
free(hn);
return 0;
}
@@ -799,7 +809,7 @@ int config_parse_timezone(
return r;
if (!timezone_is_valid(tz)) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Timezone is not valid, ignoring assignment: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Timezone is not valid, ignoring assignment: %s", rvalue);
free(tz);
return 0;
}
@@ -844,7 +854,7 @@ int config_parse_dhcp_server_dns(
return 0;
if (inet_pton(AF_INET, w, &a) <= 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse DNS server address, ignoring: %s", w);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse DNS server address, ignoring: %s", w);
continue;
}
@@ -883,7 +893,7 @@ int config_parse_dhcp_server_ntp(
r = extract_first_word(&p, &w, NULL, 0);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, r, line, "Failed to extract word, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue);
return 0;
}
@@ -891,7 +901,7 @@ int config_parse_dhcp_server_ntp(
return 0;
if (inet_pton(AF_INET, w, &a) <= 0) {
- log_syntax(unit, LOG_ERR, filename, r, line, "Failed to parse NTP server address, ignoring: %s", w);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse NTP server address, ignoring: %s", w);
continue;
}
@@ -903,3 +913,55 @@ int config_parse_dhcp_server_ntp(
n->dhcp_server_ntp = m;
}
}
+
+int config_parse_dnssec_negative_trust_anchors(
+ 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 *p = rvalue;
+ Network *n = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ if (isempty(rvalue)) {
+ n->dnssec_negative_trust_anchors = set_free_free(n->dnssec_negative_trust_anchors);
+ return 0;
+ }
+
+ for (;;) {
+ _cleanup_free_ char *w = NULL;
+
+ r = extract_first_word(&p, &w, NULL, 0);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract negative trust anchor domain, ignoring: %s", rvalue);
+ break;
+ }
+ if (r == 0)
+ break;
+
+ r = dns_name_is_valid(w);
+ if (r <= 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "%s is not a valid domain name, ignoring.", w);
+ continue;
+ }
+
+ r = set_put(n->dnssec_negative_trust_anchors, w);
+ if (r < 0)
+ return log_oom();
+ if (r > 0)
+ w = NULL;
+ }
+
+ return 0;
+}
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index 2a43b6b347..b07fa41abc 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -22,15 +22,16 @@
***/
#include "condition.h"
+#include "resolve-util.h"
typedef struct Network Network;
-#include "networkd.h"
-#include "networkd-netdev.h"
#include "networkd-address.h"
-#include "networkd-route.h"
#include "networkd-fdb.h"
+#include "networkd-netdev.h"
+#include "networkd-route.h"
#include "networkd-util.h"
+#include "networkd.h"
#define DHCP_ROUTE_METRIC 1024
#define IPV4LL_ROUTE_METRIC 2048
@@ -121,6 +122,8 @@ struct Network {
bool ip_masquerade;
int ipv6_accept_ra;
+ int ipv6_dad_transmits;
+ int ipv6_hop_limit;
union in_addr_union ipv6_token;
IPv6PrivacyExtensions ipv6_privacy_extensions;
@@ -142,6 +145,9 @@ struct Network {
char **domains, **dns, **ntp, **bind_carrier;
ResolveSupport llmnr;
+ ResolveSupport mdns;
+ DnssecMode dnssec_mode;
+ Set *dnssec_negative_trust_anchors;
LIST_FIELDS(Network, networks);
};
@@ -168,6 +174,7 @@ int config_parse_hostname(const char *unit, const char *filename, unsigned line,
int config_parse_timezone(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_server_dns(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_server_ntp(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_dnssec_negative_trust_anchors(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);
/* Legacy IPv4LL support */
int config_parse_ipv4ll(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-route.c b/src/network/networkd-route.c
index fbaad40579..798fe3f647 100644
--- a/src/network/networkd-route.c
+++ b/src/network/networkd-route.c
@@ -19,15 +19,39 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
+#include "alloc-util.h"
#include "conf-parser.h"
+#include "in-addr-util.h"
#include "netlink-util.h"
-
-#include "networkd.h"
#include "networkd-route.h"
+#include "networkd.h"
+#include "parse-util.h"
+#include "set.h"
+#include "string-util.h"
+#include "util.h"
+
+int route_new(Route **ret) {
+ _cleanup_route_free_ Route *route = NULL;
+
+ route = new0(Route, 1);
+ if (!route)
+ return -ENOMEM;
+
+ route->family = AF_UNSPEC;
+ route->scope = RT_SCOPE_UNIVERSE;
+ route->protocol = RTPROT_UNSPEC;
+ route->table = RT_TABLE_DEFAULT;
+ route->lifetime = USEC_INFINITY;
+
+ *ret = route;
+ route = NULL;
+
+ return 0;
+}
int route_new_static(Network *network, unsigned section, Route **ret) {
_cleanup_route_free_ Route *route = NULL;
+ int r;
if (section) {
route = hashmap_get(network->routes_by_section,
@@ -40,14 +64,11 @@ int route_new_static(Network *network, unsigned section, Route **ret) {
}
}
- route = new0(Route, 1);
- if (!route)
- return -ENOMEM;
+ r = route_new(&route);
+ if (r < 0)
+ return r;
- route->family = AF_UNSPEC;
- route->scope = RT_SCOPE_UNIVERSE;
route->protocol = RTPROT_STATIC;
-
route->network = network;
LIST_PREPEND(routes, network->static_routes, route);
@@ -64,23 +85,6 @@ int route_new_static(Network *network, unsigned section, Route **ret) {
return 0;
}
-int route_new_dynamic(Route **ret, unsigned char rtm_protocol) {
- _cleanup_route_free_ Route *route = NULL;
-
- route = new0(Route, 1);
- if (!route)
- return -ENOMEM;
-
- route->family = AF_UNSPEC;
- route->scope = RT_SCOPE_UNIVERSE;
- route->protocol = rtm_protocol;
-
- *ret = route;
- route = NULL;
-
- return 0;
-}
-
void route_free(Route *route) {
if (!route)
return;
@@ -93,12 +97,243 @@ void route_free(Route *route) {
UINT_TO_PTR(route->section));
}
+ if (route->link) {
+ set_remove(route->link->routes, route);
+ set_remove(route->link->routes_foreign, route);
+ }
+
+ sd_event_source_unref(route->expire);
+
free(route);
}
-int route_drop(Route *route, Link *link,
+static void route_hash_func(const void *b, struct siphash *state) {
+ const Route *route = b;
+
+ assert(route);
+
+ siphash24_compress(&route->family, sizeof(route->family), state);
+
+ switch (route->family) {
+ case AF_INET:
+ case AF_INET6:
+ /* Equality of routes are given by the 4-touple
+ (dst_prefix,dst_prefixlen,tos,priority,table) */
+ siphash24_compress(&route->dst, FAMILY_ADDRESS_SIZE(route->family), state);
+ siphash24_compress(&route->dst_prefixlen, sizeof(route->dst_prefixlen), state);
+ siphash24_compress(&route->tos, sizeof(route->tos), state);
+ siphash24_compress(&route->priority, sizeof(route->priority), state);
+ siphash24_compress(&route->table, sizeof(route->table), state);
+
+ break;
+ default:
+ /* treat any other address family as AF_UNSPEC */
+ break;
+ }
+}
+
+static int route_compare_func(const void *_a, const void *_b) {
+ const Route *a = _a, *b = _b;
+
+ if (a->family < b->family)
+ return -1;
+ if (a->family > b->family)
+ return 1;
+
+ switch (a->family) {
+ case AF_INET:
+ case AF_INET6:
+ if (a->dst_prefixlen < b->dst_prefixlen)
+ return -1;
+ if (a->dst_prefixlen > b->dst_prefixlen)
+ return 1;
+
+ if (a->tos < b->tos)
+ return -1;
+ if (a->tos > b->tos)
+ return 1;
+
+ if (a->priority < b->priority)
+ return -1;
+ if (a->priority > b->priority)
+ return 1;
+
+ if (a->table < b->table)
+ return -1;
+ if (a->table > b->table)
+ return 1;
+
+ return memcmp(&a->dst, &b->dst, FAMILY_ADDRESS_SIZE(a->family));
+ default:
+ /* treat any other address family as AF_UNSPEC */
+ return 0;
+ }
+}
+
+static const struct hash_ops route_hash_ops = {
+ .hash = route_hash_func,
+ .compare = route_compare_func
+};
+
+int route_get(Link *link,
+ int family,
+ union in_addr_union *dst,
+ unsigned char dst_prefixlen,
+ unsigned char tos,
+ uint32_t priority,
+ unsigned char table,
+ Route **ret) {
+ Route route = {
+ .family = family,
+ .dst_prefixlen = dst_prefixlen,
+ .tos = tos,
+ .priority = priority,
+ .table = table,
+ }, *existing;
+
+ assert(link);
+ assert(dst);
+ assert(ret);
+
+ route.dst = *dst;
+
+ existing = set_get(link->routes, &route);
+ if (existing) {
+ *ret = existing;
+ return 1;
+ } else {
+ existing = set_get(link->routes_foreign, &route);
+ if (!existing)
+ return -ENOENT;
+ }
+
+ *ret = existing;
+
+ return 0;
+}
+
+static int route_add_internal(Link *link, Set **routes,
+ int family,
+ union in_addr_union *dst,
+ unsigned char dst_prefixlen,
+ unsigned char tos,
+ uint32_t priority,
+ unsigned char table, Route **ret) {
+ _cleanup_route_free_ Route *route = NULL;
+ int r;
+
+ assert(link);
+ assert(routes);
+ assert(dst);
+
+ r = route_new(&route);
+ if (r < 0)
+ return r;
+
+ route->family = family;
+ route->dst = *dst;
+ route->dst_prefixlen = dst_prefixlen;
+ route->tos = tos;
+ route->priority = priority;
+ route->table = table;
+
+ r = set_ensure_allocated(routes, &route_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = set_put(*routes, route);
+ if (r < 0)
+ return r;
+
+ route->link = link;
+
+ if (ret)
+ *ret = route;
+
+ route = NULL;
+
+ return 0;
+}
+
+int route_add_foreign(Link *link,
+ int family,
+ union in_addr_union *dst,
+ unsigned char dst_prefixlen,
+ unsigned char tos,
+ uint32_t priority,
+ unsigned char table, Route **ret) {
+ return route_add_internal(link, &link->routes_foreign, family, dst, dst_prefixlen, tos, priority, table, ret);
+}
+
+int route_add(Link *link,
+ int family,
+ union in_addr_union *dst,
+ unsigned char dst_prefixlen,
+ unsigned char tos,
+ uint32_t priority,
+ unsigned char table, Route **ret) {
+ Route *route;
+ int r;
+
+ r = route_get(link, family, dst, dst_prefixlen, tos, priority, table, &route);
+ if (r == -ENOENT) {
+ /* Route does not exist, create a new one */
+ r = route_add_internal(link, &link->routes, family, dst, dst_prefixlen, tos, priority, table, &route);
+ if (r < 0)
+ return r;
+ } else if (r == 0) {
+ /* Take over a foreign route */
+ r = set_ensure_allocated(&link->routes, &route_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = set_put(link->routes, route);
+ if (r < 0)
+ return r;
+
+ set_remove(link->routes_foreign, route);
+ } else if (r == 1) {
+ /* Route exists, do nothing */
+ ;
+ } else
+ return r;
+
+ *ret = route;
+
+ return 0;
+}
+
+int route_update(Route *route,
+ union in_addr_union *src,
+ unsigned char src_prefixlen,
+ union in_addr_union *gw,
+ union in_addr_union *prefsrc,
+ unsigned char scope,
+ unsigned char protocol) {
+ assert(route);
+ assert(src);
+ assert(gw);
+ assert(prefsrc);
+
+ route->src = *src;
+ route->src_prefixlen = src_prefixlen;
+ route->gw = *gw;
+ route->prefsrc = *prefsrc;
+ route->scope = scope;
+ route->protocol = protocol;
+
+ return 0;
+}
+
+void route_drop(Route *route) {
+ assert(route);
+
+ route_free(route);
+}
+
+int route_remove(Route *route, Link *link,
sd_netlink_message_handler_t callback) {
- _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
int r;
assert(link);
@@ -113,20 +348,20 @@ int route_drop(Route *route, Link *link,
if (r < 0)
return log_error_errno(r, "Could not create RTM_DELROUTE message: %m");
- if (!in_addr_is_null(route->family, &route->in_addr)) {
+ if (!in_addr_is_null(route->family, &route->gw)) {
if (route->family == AF_INET)
- r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->in_addr.in);
+ r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->gw.in);
else if (route->family == AF_INET6)
- r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->in_addr.in6);
+ r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->gw.in6);
if (r < 0)
return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m");
}
if (route->dst_prefixlen) {
if (route->family == AF_INET)
- r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst_addr.in);
+ r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst.in);
else if (route->family == AF_INET6)
- r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst_addr.in6);
+ r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst.in6);
if (r < 0)
return log_error_errno(r, "Could not append RTA_DST attribute: %m");
@@ -137,9 +372,9 @@ int route_drop(Route *route, Link *link,
if (route->src_prefixlen) {
if (route->family == AF_INET)
- r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src_addr.in);
+ r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src.in);
else if (route->family == AF_INET6)
- r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src_addr.in6);
+ r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src.in6);
if (r < 0)
return log_error_errno(r, "Could not append RTA_DST attribute: %m");
@@ -148,11 +383,11 @@ int route_drop(Route *route, Link *link,
return log_error_errno(r, "Could not set source prefix length: %m");
}
- if (!in_addr_is_null(route->family, &route->prefsrc_addr)) {
+ if (!in_addr_is_null(route->family, &route->prefsrc)) {
if (route->family == AF_INET)
- r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in);
+ r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc.in);
else if (route->family == AF_INET6)
- r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in6);
+ r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc.in6);
if (r < 0)
return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m");
}
@@ -161,7 +396,7 @@ int route_drop(Route *route, Link *link,
if (r < 0)
return log_error_errno(r, "Could not set scope: %m");
- r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->metrics);
+ r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority);
if (r < 0)
return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m");
@@ -178,9 +413,24 @@ int route_drop(Route *route, Link *link,
return 0;
}
+int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) {
+ Route *route = userdata;
+ int r;
+
+ assert(route);
+
+ r = route_remove(route, route->link, NULL);
+ if (r < 0)
+ log_warning_errno(r, "Could not remove route: %m");
+
+ return 1;
+}
+
int route_configure(Route *route, Link *link,
sd_netlink_message_handler_t callback) {
- _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+ _cleanup_(sd_event_source_unrefp) sd_event_source *expire = NULL;
+ usec_t lifetime;
int r;
assert(link);
@@ -195,20 +445,20 @@ int route_configure(Route *route, Link *link,
if (r < 0)
return log_error_errno(r, "Could not create RTM_NEWROUTE message: %m");
- if (!in_addr_is_null(route->family, &route->in_addr)) {
+ if (!in_addr_is_null(route->family, &route->gw)) {
if (route->family == AF_INET)
- r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->in_addr.in);
+ r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->gw.in);
else if (route->family == AF_INET6)
- r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->in_addr.in6);
+ r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->gw.in6);
if (r < 0)
return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m");
}
if (route->dst_prefixlen) {
if (route->family == AF_INET)
- r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst_addr.in);
+ r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst.in);
else if (route->family == AF_INET6)
- r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst_addr.in6);
+ r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst.in6);
if (r < 0)
return log_error_errno(r, "Could not append RTA_DST attribute: %m");
@@ -219,9 +469,9 @@ int route_configure(Route *route, Link *link,
if (route->src_prefixlen) {
if (route->family == AF_INET)
- r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src_addr.in);
+ r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src.in);
else if (route->family == AF_INET6)
- r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src_addr.in6);
+ r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src.in6);
if (r < 0)
return log_error_errno(r, "Could not append RTA_SRC attribute: %m");
@@ -230,11 +480,11 @@ int route_configure(Route *route, Link *link,
return log_error_errno(r, "Could not set source prefix length: %m");
}
- if (!in_addr_is_null(route->family, &route->prefsrc_addr)) {
+ if (!in_addr_is_null(route->family, &route->prefsrc)) {
if (route->family == AF_INET)
- r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in);
+ r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc.in);
else if (route->family == AF_INET6)
- r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in6);
+ r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc.in6);
if (r < 0)
return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m");
}
@@ -243,10 +493,18 @@ int route_configure(Route *route, Link *link,
if (r < 0)
return log_error_errno(r, "Could not set scope: %m");
- r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->metrics);
+ r = sd_rtnl_message_route_set_flags(req, route->flags);
+ if (r < 0)
+ return log_error_errno(r, "Colud not set flags: %m");
+
+ r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority);
if (r < 0)
return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m");
+ r = sd_netlink_message_append_u8(req, RTA_PREF, route->pref);
+ if (r < 0)
+ return log_error_errno(r, "Could not append RTA_PREF attribute: %m");
+
r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
if (r < 0)
return log_error_errno(r, "Could not append RTA_OIF attribute: %m");
@@ -257,6 +515,26 @@ int route_configure(Route *route, Link *link,
link_ref(link);
+ lifetime = route->lifetime;
+
+ r = route_add(link, route->family, &route->dst, route->dst_prefixlen, route->tos, route->priority, route->table, &route);
+ if (r < 0)
+ return log_error_errno(r, "Could not add route: %m");
+
+ /* TODO: drop expiration handling once it can be pushed into the kernel */
+ route->lifetime = lifetime;
+
+ if (route->lifetime != USEC_INFINITY) {
+ r = sd_event_add_time(link->manager->event, &expire, clock_boottime_or_monotonic(),
+ route->lifetime, 0, route_expire_handler, route);
+ if (r < 0)
+ return log_error_errno(r, "Could not arm expiration timer: %m");
+ }
+
+ sd_event_source_unref(route->expire);
+ route->expire = expire;
+ expire = NULL;
+
return 0;
}
@@ -294,13 +572,52 @@ int config_parse_gateway(const char *unit,
r = in_addr_from_string_auto(rvalue, &f, &buffer);
if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Route is invalid, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+
+ n->family = f;
+ n->gw = buffer;
+ n = NULL;
+
+ return 0;
+}
+
+int config_parse_preferred_src(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_route_free_ Route *n = NULL;
+ union in_addr_union buffer;
+ int r, f;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = route_new_static(network, section_line, &n);
+ if (r < 0)
+ return r;
+
+ r = in_addr_from_string_auto(rvalue, &f, &buffer);
+ if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Route is invalid, ignoring assignment: %s", rvalue);
+ "Preferred source is invalid, ignoring assignment: %s", rvalue);
return 0;
}
n->family = f;
- n->in_addr = buffer;
+ n->prefsrc = buffer;
n = NULL;
return 0;
@@ -345,14 +662,12 @@ int config_parse_destination(const char *unit,
r = in_addr_from_string_auto(address, &f, &buffer);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Destination is invalid, ignoring assignment: %s", address);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Destination is invalid, ignoring assignment: %s", address);
return 0;
}
if (f != AF_INET && f != AF_INET6) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Unknown address family, ignoring assignment: %s", address);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown address family, ignoring assignment: %s", address);
return 0;
}
@@ -360,8 +675,7 @@ int config_parse_destination(const char *unit,
if (e) {
r = safe_atou8(e + 1, &prefixlen);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Route destination prefix length is invalid, ignoring assignment: %s", e + 1);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Route destination prefix length is invalid, ignoring assignment: %s", e + 1);
return 0;
}
} else {
@@ -377,10 +691,10 @@ int config_parse_destination(const char *unit,
n->family = f;
if (streq(lvalue, "Destination")) {
- n->dst_addr = buffer;
+ n->dst = buffer;
n->dst_prefixlen = prefixlen;
} else if (streq(lvalue, "Source")) {
- n->src_addr = buffer;
+ n->src = buffer;
n->src_prefixlen = prefixlen;
} else
assert_not_reached(lvalue);
@@ -414,9 +728,9 @@ int config_parse_route_priority(const char *unit,
if (r < 0)
return r;
- r = config_parse_unsigned(unit, filename, line, section,
- section_line, lvalue, ltype,
- rvalue, &n->metrics, userdata);
+ r = config_parse_uint32(unit, filename, line, section,
+ section_line, lvalue, ltype,
+ rvalue, &n->priority, userdata);
if (r < 0)
return r;
@@ -456,8 +770,7 @@ int config_parse_route_scope(const char *unit,
else if (streq(rvalue, "global"))
n->scope = RT_SCOPE_UNIVERSE;
else {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Unknown route scope: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown route scope: %s", rvalue);
return 0;
}
diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h
index d090b9c91e..37c12907d7 100644
--- a/src/network/networkd-route.h
+++ b/src/network/networkd-route.h
@@ -23,38 +23,56 @@
typedef struct Route Route;
-#include "networkd.h"
#include "networkd-network.h"
+#include "networkd.h"
struct Route {
Network *network;
unsigned section;
+ Link *link;
+
int family;
unsigned char dst_prefixlen;
unsigned char src_prefixlen;
unsigned char scope;
- uint32_t metrics;
unsigned char protocol; /* RTPROT_* */
+ unsigned char tos;
+ uint32_t priority; /* note that ip(8) calls this 'metric' */
+ unsigned char table;
+ unsigned char pref;
+ unsigned flags;
- union in_addr_union in_addr;
- union in_addr_union dst_addr;
- union in_addr_union src_addr;
- union in_addr_union prefsrc_addr;
+ union in_addr_union gw;
+ union in_addr_union dst;
+ union in_addr_union src;
+ union in_addr_union prefsrc;
+
+ usec_t lifetime;
+ sd_event_source *expire;
LIST_FIELDS(Route, routes);
};
int route_new_static(Network *network, unsigned section, Route **ret);
-int route_new_dynamic(Route **ret, unsigned char rtm_protocol);
+int route_new(Route **ret);
void route_free(Route *route);
int route_configure(Route *route, Link *link, sd_netlink_message_handler_t callback);
-int route_drop(Route *route, Link *link, sd_netlink_message_handler_t callback);
+int route_remove(Route *route, Link *link, sd_netlink_message_handler_t callback);
+
+int route_get(Link *link, int family, union in_addr_union *dst, unsigned char dst_prefixlen, unsigned char tos, uint32_t priority, unsigned char table, Route **ret);
+int route_add(Link *link, int family, union in_addr_union *dst, unsigned char dst_prefixlen, unsigned char tos, uint32_t priority, unsigned char table, Route **ret);
+int route_add_foreign(Link *link, int family, union in_addr_union *dst, unsigned char dst_prefixlen, unsigned char tos, uint32_t priority, unsigned char table, Route **ret);
+int route_update(Route *route, union in_addr_union *src, unsigned char src_prefixlen, union in_addr_union *gw, union in_addr_union *prefsrc, unsigned char scope, unsigned char protocol);
+void route_drop(Route *route);
+
+int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata);
DEFINE_TRIVIAL_CLEANUP_FUNC(Route*, route_free);
#define _cleanup_route_free_ _cleanup_(route_freep)
int config_parse_gateway(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_preferred_src(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_destination(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_route_priority(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_route_scope(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-util.c b/src/network/networkd-util.c
index a41cd86239..93135bb658 100644
--- a/src/network/networkd-util.c
+++ b/src/network/networkd-util.c
@@ -19,10 +19,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
#include "conf-parser.h"
-
#include "networkd-util.h"
+#include "parse-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "util.h"
const char *address_family_boolean_to_string(AddressFamilyBoolean b) {
if (b == ADDRESS_FAMILY_YES ||
@@ -77,12 +79,20 @@ int config_parse_address_family_boolean_with_kernel(
assert(rvalue);
assert(data);
+ /* This function is mostly obsolete now. It simply redirects
+ * "kernel" to "no". In older networkd versions we used to
+ * distuingish IPForward=off from IPForward=kernel, where the
+ * former would explicitly turn off forwarding while the
+ * latter would simply not touch the setting. But that logic
+ * is gone, hence silently accept the old setting, but turn it
+ * to "no". */
+
s = address_family_boolean_from_string(rvalue);
if (s < 0) {
if (streq(rvalue, "kernel"))
- s = _ADDRESS_FAMILY_BOOLEAN_INVALID;
+ s = ADDRESS_FAMILY_NO;
else {
- log_syntax(unit, LOG_ERR, filename, line, s, "Failed to parse IPForwarding= option, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IPForward= option, ignoring: %s", rvalue);
return 0;
}
}
@@ -91,54 +101,3 @@ int config_parse_address_family_boolean_with_kernel(
return 0;
}
-
-static const char* const resolve_support_table[_RESOLVE_SUPPORT_MAX] = {
- [RESOLVE_SUPPORT_NO] = "no",
- [RESOLVE_SUPPORT_YES] = "yes",
- [RESOLVE_SUPPORT_RESOLVE] = "resolve",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(resolve_support, ResolveSupport);
-
-int config_parse_resolve(
- 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) {
-
- ResolveSupport *resolve = data;
- int k;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(resolve);
-
- /* Our enum shall be a superset of booleans, hence first try
- * to parse as boolean, and then as enum */
-
- k = parse_boolean(rvalue);
- if (k > 0)
- *resolve = RESOLVE_SUPPORT_YES;
- else if (k == 0)
- *resolve = RESOLVE_SUPPORT_NO;
- else {
- ResolveSupport s;
-
- s = resolve_support_from_string(rvalue);
- if (s < 0){
- log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse %s= option, ignoring: %s", lvalue, rvalue);
- return 0;
- }
-
- *resolve = s;
- }
-
- return 0;
-}
diff --git a/src/network/networkd-util.h b/src/network/networkd-util.h
index cc41aae85a..021ce4b128 100644
--- a/src/network/networkd-util.h
+++ b/src/network/networkd-util.h
@@ -33,20 +33,8 @@ typedef enum AddressFamilyBoolean {
_ADDRESS_FAMILY_BOOLEAN_INVALID = -1,
} AddressFamilyBoolean;
-typedef enum ResolveSupport {
- RESOLVE_SUPPORT_NO,
- RESOLVE_SUPPORT_YES,
- RESOLVE_SUPPORT_RESOLVE,
- _RESOLVE_SUPPORT_MAX,
- _RESOLVE_SUPPORT_INVALID = -1,
-} ResolveSupport;
-
-int config_parse_resolve(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_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);
int config_parse_address_family_boolean_with_kernel(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* resolve_support_to_string(ResolveSupport i) _const_;
-ResolveSupport resolve_support_from_string(const char *s) _pure_;
-
const char *address_family_boolean_to_string(AddressFamilyBoolean b) _const_;
AddressFamilyBoolean address_family_boolean_from_string(const char *s) _const_;
diff --git a/src/network/networkd-wait-online-link.c b/src/network/networkd-wait-online-link.c
index cacb4c257e..c2779ff773 100644
--- a/src/network/networkd-wait-online-link.c
+++ b/src/network/networkd-wait-online-link.c
@@ -20,10 +20,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-
#include "sd-network.h"
+#include "alloc-util.h"
#include "networkd-wait-online-link.h"
+#include "string-util.h"
int link_new(Manager *m, Link **ret, int ifindex, const char *ifname) {
_cleanup_(link_freep) Link *l = NULL;
diff --git a/src/network/networkd-wait-online-manager.c b/src/network/networkd-wait-online-manager.c
index 112d92a568..c70b370012 100644
--- a/src/network/networkd-wait-online-manager.c
+++ b/src/network/networkd-wait-online-manager.c
@@ -23,14 +23,13 @@
#include <linux/if.h>
#include <fnmatch.h>
+#include "alloc-util.h"
#include "netlink-util.h"
-
#include "network-internal.h"
#include "networkd-wait-online-link.h"
#include "networkd-wait-online.h"
-
-#include "util.h"
#include "time-util.h"
+#include "util.h"
bool manager_ignore_link(Manager *m, Link *link) {
char **ignore;
@@ -176,7 +175,7 @@ static int on_rtnl_event(sd_netlink *rtnl, sd_netlink_message *mm, void *userdat
}
static int manager_rtnl_listen(Manager *m) {
- _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
sd_netlink_message *i;
int r;
diff --git a/src/network/networkd-wait-online.c b/src/network/networkd-wait-online.c
index d958b48771..3220c4b7ef 100644
--- a/src/network/networkd-wait-online.c
+++ b/src/network/networkd-wait-online.c
@@ -21,10 +21,10 @@
#include <getopt.h>
#include "sd-daemon.h"
-#include "strv.h"
-#include "build.h"
-#include "signal-util.h"
+
#include "networkd-wait-online.h"
+#include "signal-util.h"
+#include "strv.h"
static bool arg_quiet = false;
static usec_t arg_timeout = 120 * USEC_PER_SEC;
@@ -79,9 +79,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case 'i':
if (strv_extend(&arg_interfaces, optarg) < 0)
diff --git a/src/network/networkd.c b/src/network/networkd.c
index e6259043fa..ef394e0c04 100644
--- a/src/network/networkd.c
+++ b/src/network/networkd.c
@@ -20,9 +20,11 @@
***/
#include "sd-daemon.h"
-#include "capability.h"
-#include "signal-util.h"
+
+#include "capability-util.h"
#include "networkd.h"
+#include "signal-util.h"
+#include "user-util.h"
int main(int argc, char *argv[]) {
_cleanup_manager_free_ Manager *m = NULL;
@@ -107,6 +109,12 @@ int main(int argc, char *argv[]) {
goto out;
}
+ r = manager_rtnl_enumerate_routes(m);
+ if (r < 0) {
+ log_error_errno(r, "Could not enumerate routes: %m");
+ goto out;
+ }
+
log_info("Enumeration completed");
sd_notify(false,
diff --git a/src/network/networkd.h b/src/network/networkd.h
index eea57ac158..8086e528bf 100644
--- a/src/network/networkd.h
+++ b/src/network/networkd.h
@@ -23,19 +23,19 @@
#include <arpa/inet.h>
+#include "sd-bus.h"
#include "sd-event.h"
#include "sd-netlink.h"
-#include "sd-bus.h"
-#include "udev.h"
#include "hashmap.h"
#include "list.h"
+#include "udev.h"
typedef struct Manager Manager;
-#include "networkd-network.h"
#include "networkd-address-pool.h"
#include "networkd-link.h"
+#include "networkd-network.h"
#include "networkd-util.h"
struct Manager {
@@ -48,7 +48,10 @@ struct Manager {
struct udev_monitor *udev_monitor;
sd_event_source *udev_event_source;
- bool enumerating;
+ bool enumerating:1;
+ bool dirty:1;
+
+ Set *dirty_links;
char *state_file;
LinkOperationalState operational_state;
@@ -79,9 +82,13 @@ bool manager_should_reload(Manager *m);
int manager_rtnl_enumerate_links(Manager *m);
int manager_rtnl_enumerate_addresses(Manager *m);
+int manager_rtnl_enumerate_routes(Manager *m);
+
+int manager_rtnl_process_address(sd_netlink *nl, sd_netlink_message *message, void *userdata);
+int manager_rtnl_process_route(sd_netlink *nl, sd_netlink_message *message, void *userdata);
int manager_send_changed(Manager *m, const char *property, ...) _sentinel_;
-int manager_save(Manager *m);
+void manager_dirty(Manager *m);
int manager_address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found);
diff --git a/src/network/test-network-tables.c b/src/network/test-network-tables.c
index 438214015d..ecbbe6c3c9 100644
--- a/src/network/test-network-tables.c
+++ b/src/network/test-network-tables.c
@@ -1,11 +1,10 @@
-#include "networkd.h"
-#include "networkd-netdev-bond.h"
-#include "networkd-netdev-macvlan.h"
#include "dhcp6-internal.h"
#include "dhcp6-protocol.h"
-#include "netlink-internal.h"
#include "ethtool-util.h"
-
+#include "netlink-internal.h"
+#include "networkd-netdev-bond.h"
+#include "networkd-netdev-macvlan.h"
+#include "networkd.h"
#include "test-tables.h"
int main(int argc, char **argv) {
diff --git a/src/network/test-network.c b/src/network/test-network.c
index 5909cc790e..a1a77b6867 100644
--- a/src/network/test-network.c
+++ b/src/network/test-network.c
@@ -19,9 +19,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "networkd.h"
-#include "network-internal.h"
+#include "alloc-util.h"
#include "dhcp-lease-internal.h"
+#include "network-internal.h"
+#include "networkd.h"
static void test_deserialize_in_addr(void) {
_cleanup_free_ struct in_addr *addresses = NULL;
@@ -143,8 +144,8 @@ static void test_network_get(Manager *manager, struct udev_device *loopback) {
static void test_address_equality(void) {
_cleanup_address_free_ Address *a1 = NULL, *a2 = NULL;
- assert_se(address_new_dynamic(&a1) >= 0);
- assert_se(address_new_dynamic(&a2) >= 0);
+ assert_se(address_new(&a1) >= 0);
+ assert_se(address_new(&a2) >= 0);
assert_se(address_equal(NULL, NULL));
assert_se(!address_equal(a1, NULL));
@@ -158,17 +159,18 @@ static void test_address_equality(void) {
assert_se(address_equal(a1, a2));
assert_se(inet_pton(AF_INET, "192.168.3.9", &a1->in_addr.in));
- assert_se(address_equal(a1, a2));
+ assert_se(!address_equal(a1, a2));
assert_se(inet_pton(AF_INET, "192.168.3.9", &a2->in_addr.in));
assert_se(address_equal(a1, a2));
+ assert_se(inet_pton(AF_INET, "192.168.3.10", &a1->in_addr_peer.in));
+ assert_se(address_equal(a1, a2));
+ assert_se(inet_pton(AF_INET, "192.168.3.11", &a2->in_addr_peer.in));
+ assert_se(address_equal(a1, a2));
a1->prefixlen = 10;
assert_se(!address_equal(a1, a2));
a2->prefixlen = 10;
assert_se(address_equal(a1, a2));
- assert_se(inet_pton(AF_INET, "192.168.3.10", &a2->in_addr.in));
- assert_se(address_equal(a1, a2));
-
a1->family = AF_INET6;
assert_se(!address_equal(a1, a2));
diff --git a/src/notify/notify.c b/src/notify/notify.c
index 7d53cb6d75..b144554702 100644
--- a/src/notify/notify.c
+++ b/src/notify/notify.c
@@ -27,10 +27,12 @@
#include "sd-daemon.h"
-#include "build.h"
+#include "alloc-util.h"
#include "env-util.h"
#include "formats-util.h"
#include "log.h"
+#include "parse-util.h"
+#include "string-util.h"
#include "strv.h"
#include "util.h"
@@ -85,9 +87,7 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case ARG_READY:
arg_ready = true;
diff --git a/src/nspawn/nspawn-cgroup.c b/src/nspawn/nspawn-cgroup.c
index c0e9ccd7a4..3c0e26ea5a 100644
--- a/src/nspawn/nspawn-cgroup.c
+++ b/src/nspawn/nspawn-cgroup.c
@@ -21,13 +21,15 @@
#include <sys/mount.h>
-#include "util.h"
-#include "strv.h"
-#include "mkdir.h"
-#include "fileio.h"
+#include "alloc-util.h"
#include "cgroup-util.h"
-
+#include "fd-util.h"
+#include "fileio.h"
+#include "mkdir.h"
#include "nspawn-cgroup.h"
+#include "string-util.h"
+#include "strv.h"
+#include "util.h"
int chown_cgroup(pid_t pid, uid_t uid_shift) {
_cleanup_free_ char *path = NULL, *fs = NULL;
@@ -52,6 +54,7 @@ int chown_cgroup(pid_t pid, uid_t uid_shift) {
"tasks",
"notify_on_release",
"cgroup.procs",
+ "cgroup.events",
"cgroup.clone_children",
"cgroup.controllers",
"cgroup.subtree_control",
diff --git a/src/nspawn/nspawn-cgroup.h b/src/nspawn/nspawn-cgroup.h
index 985fdfaad5..4e8db63750 100644
--- a/src/nspawn/nspawn-cgroup.h
+++ b/src/nspawn/nspawn-cgroup.h
@@ -21,8 +21,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
#include <stdbool.h>
+#include <sys/types.h>
int chown_cgroup(pid_t pid, uid_t uid_shift);
int sync_cgroup(pid_t pid, bool unified_requested);
diff --git a/src/nspawn/nspawn-expose-ports.c b/src/nspawn/nspawn-expose-ports.c
index 9e63d88b69..89e5c57db3 100644
--- a/src/nspawn/nspawn-expose-ports.c
+++ b/src/nspawn/nspawn-expose-ports.c
@@ -21,13 +21,17 @@
#include "sd-netlink.h"
-#include "util.h"
-#include "in-addr-util.h"
+#include "alloc-util.h"
+#include "fd-util.h"
#include "firewall-util.h"
+#include "in-addr-util.h"
#include "local-addresses.h"
#include "netlink-util.h"
-
#include "nspawn-expose-ports.h"
+#include "parse-util.h"
+#include "socket-util.h"
+#include "string-util.h"
+#include "util.h"
int expose_port_parse(ExposePort **l, const char *s) {
@@ -194,7 +198,7 @@ int expose_port_send_rtnl(int send_fd) {
/* Store away the fd in the socket, so that it stays open as
* long as we run the child */
- r = send_one_fd(send_fd, fd);
+ r = send_one_fd(send_fd, fd, 0);
if (r < 0)
return log_error_errno(r, "Failed to send netlink fd: %m");
@@ -207,14 +211,14 @@ int expose_port_watch_rtnl(
sd_netlink_message_handler_t handler,
union in_addr_union *exposed,
sd_netlink **ret) {
- _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
int fd, r;
assert(event);
assert(recv_fd >= 0);
assert(ret);
- fd = receive_one_fd(recv_fd);
+ fd = receive_one_fd(recv_fd, 0);
if (fd < 0)
return log_error_errno(fd, "Failed to recv netlink fd: %m");
diff --git a/src/nspawn/nspawn-expose-ports.h b/src/nspawn/nspawn-expose-ports.h
index 39cec28695..cb7340bad7 100644
--- a/src/nspawn/nspawn-expose-ports.h
+++ b/src/nspawn/nspawn-expose-ports.h
@@ -25,8 +25,9 @@
#include "sd-event.h"
#include "sd-netlink.h"
-#include "list.h"
+
#include "in-addr-util.h"
+#include "list.h"
typedef struct ExposePort {
int protocol;
diff --git a/src/nspawn/nspawn-gperf.gperf b/src/nspawn/nspawn-gperf.gperf
index b5127a387c..58f9f4c635 100644
--- a/src/nspawn/nspawn-gperf.gperf
+++ b/src/nspawn/nspawn-gperf.gperf
@@ -15,24 +15,25 @@ struct ConfigPerfItem;
%struct-type
%includes
%%
-Exec.Boot, config_parse_tristate, 0, offsetof(Settings, boot)
-Exec.Parameters, config_parse_strv, 0, offsetof(Settings, parameters)
-Exec.Environment, config_parse_strv, 0, offsetof(Settings, environment)
-Exec.User, config_parse_string, 0, offsetof(Settings, user)
-Exec.Capability, config_parse_capability, 0, offsetof(Settings, capability)
-Exec.DropCapability, config_parse_capability, 0, offsetof(Settings, drop_capability)
-Exec.KillSignal, config_parse_signal, 0, offsetof(Settings, kill_signal)
-Exec.Personality, config_parse_personality, 0, offsetof(Settings, personality)
-Exec.MachineID, config_parse_id128, 0, offsetof(Settings, machine_id)
-Files.ReadOnly, config_parse_tristate, 0, offsetof(Settings, read_only)
-Files.Volatile, config_parse_volatile_mode, 0, offsetof(Settings, volatile_mode)
-Files.Bind, config_parse_bind, 0, 0
-Files.BindReadOnly, config_parse_bind, 1, 0
-Files.TemporaryFileSystem, config_parse_tmpfs, 0, 0
-Network.Private, config_parse_tristate, 0, offsetof(Settings, private_network)
-Network.Interface, config_parse_strv, 0, offsetof(Settings, network_interfaces)
-Network.MACVLAN, config_parse_strv, 0, offsetof(Settings, network_macvlan)
-Network.IPVLAN, config_parse_strv, 0, offsetof(Settings, network_ipvlan)
-Network.VirtualEthernet, config_parse_tristate, 0, offsetof(Settings, network_veth)
-Network.Bridge, config_parse_string, 0, offsetof(Settings, network_bridge)
-Network.Port, config_parse_expose_port, 0, 0
+Exec.Boot, config_parse_tristate, 0, offsetof(Settings, boot)
+Exec.Parameters, config_parse_strv, 0, offsetof(Settings, parameters)
+Exec.Environment, config_parse_strv, 0, offsetof(Settings, environment)
+Exec.User, config_parse_string, 0, offsetof(Settings, user)
+Exec.Capability, config_parse_capability, 0, offsetof(Settings, capability)
+Exec.DropCapability, config_parse_capability, 0, offsetof(Settings, drop_capability)
+Exec.KillSignal, config_parse_signal, 0, offsetof(Settings, kill_signal)
+Exec.Personality, config_parse_personality, 0, offsetof(Settings, personality)
+Exec.MachineID, config_parse_id128, 0, offsetof(Settings, machine_id)
+Files.ReadOnly, config_parse_tristate, 0, offsetof(Settings, read_only)
+Files.Volatile, config_parse_volatile_mode, 0, offsetof(Settings, volatile_mode)
+Files.Bind, config_parse_bind, 0, 0
+Files.BindReadOnly, config_parse_bind, 1, 0
+Files.TemporaryFileSystem, config_parse_tmpfs, 0, 0
+Network.Private, config_parse_tristate, 0, offsetof(Settings, private_network)
+Network.Interface, config_parse_strv, 0, offsetof(Settings, network_interfaces)
+Network.MACVLAN, config_parse_strv, 0, offsetof(Settings, network_macvlan)
+Network.IPVLAN, config_parse_strv, 0, offsetof(Settings, network_ipvlan)
+Network.VirtualEthernet, config_parse_tristate, 0, offsetof(Settings, network_veth)
+Network.VirtualEthernetExtra, config_parse_veth_extra, 0, 0
+Network.Bridge, config_parse_string, 0, offsetof(Settings, network_bridge)
+Network.Port, config_parse_expose_port, 0, 0
diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c
index 2bca39f45d..c8e627ac78 100644
--- a/src/nspawn/nspawn-mount.c
+++ b/src/nspawn/nspawn-mount.c
@@ -20,17 +20,25 @@
***/
#include <sys/mount.h>
+#include <linux/magic.h>
-#include "util.h"
-#include "rm-rf.h"
-#include "strv.h"
-#include "path-util.h"
-#include "mkdir.h"
-#include "label.h"
-#include "set.h"
+#include "alloc-util.h"
#include "cgroup-util.h"
-
+#include "escape.h"
+#include "fs-util.h"
+#include "label.h"
+#include "mkdir.h"
+#include "mount-util.h"
#include "nspawn-mount.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "rm-rf.h"
+#include "set.h"
+#include "stat-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "user-util.h"
+#include "util.h"
CustomMount* custom_mount_add(CustomMount **l, unsigned *n, CustomMountType t) {
CustomMount *c, *ret;
@@ -216,8 +224,67 @@ static int tmpfs_patch_options(
return !!buf;
}
+int mount_sysfs(const char *dest) {
+ const char *full, *top, *x;
+ int r;
+
+ top = prefix_roota(dest, "/sys");
+ r = path_check_fstype(top, SYSFS_MAGIC);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine filesystem type of %s: %m", top);
+ /* /sys might already be mounted as sysfs by the outer child in the
+ * !netns case. In this case, it's all good. Don't touch it because we
+ * don't have the right to do so, see https://github.com/systemd/systemd/issues/1555.
+ */
+ if (r > 0)
+ return 0;
+
+ full = prefix_roota(top, "/full");
+
+ (void) mkdir(full, 0755);
+
+ if (mount("sysfs", full, "sysfs", MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) < 0)
+ return log_error_errno(errno, "Failed to mount sysfs to %s: %m", full);
+
+ FOREACH_STRING(x, "block", "bus", "class", "dev", "devices", "kernel") {
+ _cleanup_free_ char *from = NULL, *to = NULL;
+
+ from = prefix_root(full, x);
+ if (!from)
+ return log_oom();
+
+ to = prefix_root(top, x);
+ if (!to)
+ return log_oom();
+
+ (void) mkdir(to, 0755);
+
+ if (mount(from, to, NULL, MS_BIND, NULL) < 0)
+ return log_error_errno(errno, "Failed to mount /sys/%s into place: %m", x);
+
+ if (mount(NULL, to, NULL, MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, NULL) < 0)
+ return log_error_errno(errno, "Failed to mount /sys/%s read-only: %m", x);
+ }
+
+ if (umount(full) < 0)
+ return log_error_errno(errno, "Failed to unmount %s: %m", full);
+
+ if (rmdir(full) < 0)
+ return log_error_errno(errno, "Failed to remove %s: %m", full);
+
+ x = prefix_roota(top, "/fs/kdbus");
+ (void) mkdir(x, 0755);
+
+ if (mount(NULL, top, NULL, MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, NULL) < 0)
+ return log_error_errno(errno, "Failed to make %s read-only: %m", top);
+
+ return 0;
+}
+
int mount_all(const char *dest,
- bool userns, uid_t uid_shift, uid_t uid_range,
+ bool use_userns, bool in_userns,
+ bool use_netns,
+ uid_t uid_shift, uid_t uid_range,
const char *selinux_apifs_context) {
typedef struct MountPoint {
@@ -227,21 +294,23 @@ int mount_all(const char *dest,
const char *options;
unsigned long flags;
bool fatal;
- bool userns;
+ bool in_userns;
+ bool use_netns;
} MountPoint;
static const MountPoint mount_table[] = {
- { "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_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, true, true }, /* Then, make it r/o */
- { "sysfs", "/sys", "sysfs", NULL, MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, 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 },
+ { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, true, true, false },
+ { "/proc/sys", "/proc/sys", NULL, NULL, MS_BIND, true, true, false }, /* Bind mount first */
+ { NULL, "/proc/sys", NULL, NULL, MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, true, true, false }, /* Then, make it r/o */
+ { "tmpfs", "/sys", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV, true, false, true },
+ { "sysfs", "/sys", "sysfs", NULL, MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, true, false, false },
+ { "tmpfs", "/dev", "tmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, true, false, false },
+ { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true, false, false },
+ { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true, false, false },
+ { "tmpfs", "/tmp", "tmpfs", "mode=1777", MS_STRICTATIME, true, false, false },
#ifdef HAVE_SELINUX
- { "/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_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, false, false }, /* Then, make it r/o */
+ { "/sys/fs/selinux", "/sys/fs/selinux", NULL, NULL, MS_BIND, false, false, false }, /* Bind mount first */
+ { NULL, "/sys/fs/selinux", NULL, NULL, MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, false, false, false }, /* Then, make it r/o */
#endif
};
@@ -252,7 +321,10 @@ int mount_all(const char *dest,
_cleanup_free_ char *where = NULL, *options = NULL;
const char *o;
- if (userns != mount_table[k].userns)
+ if (in_userns != mount_table[k].in_userns)
+ continue;
+
+ if (!use_netns && mount_table[k].use_netns)
continue;
where = prefix_root(dest, mount_table[k].where);
@@ -278,7 +350,7 @@ int mount_all(const char *dest,
o = mount_table[k].options;
if (streq_ptr(mount_table[k].type, "tmpfs")) {
- r = tmpfs_patch_options(o, userns, uid_shift, uid_range, selinux_apifs_context, &options);
+ r = tmpfs_patch_options(o, use_userns, uid_shift, uid_range, selinux_apifs_context, &options);
if (r < 0)
return log_oom();
if (r > 0)
@@ -369,8 +441,7 @@ static int mount_bind(const char *dest, CustomMount *m) {
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;
+ return log_error_errno(errno, "Failed to stat %s: %m", where);
}
/* Create the mount point. Any non-directory file can be
@@ -534,7 +605,7 @@ static int mount_legacy_cgroup_hierarchy(const char *dest, const char *controlle
char *to;
int r;
- to = strjoina(dest, "/sys/fs/cgroup/", hierarchy);
+ to = strjoina(strempty(dest), "/sys/fs/cgroup/", hierarchy);
r = path_is_mount_point(to, 0);
if (r < 0 && r != -ENOENT)
@@ -569,6 +640,8 @@ static int mount_legacy_cgroups(
cgroup_root = prefix_roota(dest, "/sys/fs/cgroup");
+ (void) mkdir_p(cgroup_root, 0755);
+
/* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */
r = path_is_mount_point(cgroup_root, AT_SYMLINK_FOLLOW);
if (r < 0)
@@ -661,13 +734,15 @@ static int mount_unified_cgroups(const char *dest) {
assert(dest);
- p = strjoina(dest, "/sys/fs/cgroup");
+ p = prefix_roota(dest, "/sys/fs/cgroup");
+
+ (void) mkdir_p(p, 0755);
r = path_is_mount_point(p, AT_SYMLINK_FOLLOW);
if (r < 0)
return log_error_errno(r, "Failed to determine if %s is mounted already: %m", p);
if (r > 0) {
- p = strjoina(dest, "/sys/fs/cgroup/cgroup.procs");
+ p = prefix_roota(dest, "/sys/fs/cgroup/cgroup.procs");
if (access(p, F_OK) >= 0)
return 0;
if (errno != ENOENT)
diff --git a/src/nspawn/nspawn-mount.h b/src/nspawn/nspawn-mount.h
index 5abd44cc4b..bdab23bcca 100644
--- a/src/nspawn/nspawn-mount.h
+++ b/src/nspawn/nspawn-mount.h
@@ -57,7 +57,8 @@ int tmpfs_mount_parse(CustomMount **l, unsigned *n, const char *s);
int custom_mount_compare(const void *a, const void *b);
-int mount_all(const char *dest, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context);
+int mount_all(const char *dest, bool use_userns, bool in_userns, bool use_netns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context);
+int mount_sysfs(const char *dest);
int mount_cgroups(const char *dest, bool unified_requested, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context);
int mount_systemd_cgroup_writable(const char *dest, bool unified_requested);
diff --git a/src/nspawn/nspawn-network.c b/src/nspawn/nspawn-network.c
index 74abe5379a..3104c8e953 100644
--- a/src/nspawn/nspawn-network.c
+++ b/src/nspawn/nspawn-network.c
@@ -22,20 +22,23 @@
#include <linux/veth.h>
#include <net/if.h>
+#include "libudev.h"
#include "sd-id128.h"
#include "sd-netlink.h"
-#include "libudev.h"
-#include "util.h"
+#include "alloc-util.h"
#include "ether-addr-util.h"
-#include "siphash24.h"
#include "netlink-util.h"
-#include "udev-util.h"
-
#include "nspawn-network.h"
+#include "siphash24.h"
+#include "string-util.h"
+#include "udev-util.h"
+#include "util.h"
#define HOST_HASH_KEY SD_ID128_MAKE(1a,37,6f,c7,46,ec,45,0b,ad,a3,d5,31,06,60,5d,b1)
#define CONTAINER_HASH_KEY SD_ID128_MAKE(c3,c4,f9,19,b5,57,b2,1c,e6,cf,14,27,03,9c,ee,a2)
+#define VETH_EXTRA_HOST_HASH_KEY SD_ID128_MAKE(48,c7,f6,b7,ea,9d,4c,9e,b7,28,d4,de,91,d5,bf,66)
+#define VETH_EXTRA_CONTAINER_HASH_KEY SD_ID128_MAKE(af,50,17,61,ce,f9,4d,35,84,0d,2b,20,54,be,ce,59)
#define MACVLAN_HASH_KEY SD_ID128_MAKE(00,13,6d,bc,66,83,44,81,bb,0c,f9,51,1f,24,a6,6f)
static int generate_mac(
@@ -44,7 +47,7 @@ static int generate_mac(
sd_id128_t hash_key,
uint64_t idx) {
- uint8_t result[8];
+ uint64_t result;
size_t l, sz;
uint8_t *v, *i;
int r;
@@ -71,10 +74,10 @@ static int generate_mac(
/* Let's hash the host machine ID plus the container name. We
* use a fixed, but originally randomly created hash key here. */
- siphash24(result, v, sz, hash_key.bytes);
+ result = htole64(siphash24(v, sz, hash_key.bytes));
assert_cc(ETH_ALEN <= sizeof(result));
- memcpy(mac->ether_addr_octet, result, ETH_ALEN);
+ memcpy(mac->ether_addr_octet, &result, ETH_ALEN);
/* see eth_random_addr in the kernel */
mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
@@ -83,42 +86,32 @@ static int generate_mac(
return 0;
}
-int setup_veth(const char *machine_name,
- pid_t pid,
- char iface_name[IFNAMSIZ],
- bool bridge) {
-
- _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
- _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
- struct ether_addr mac_host, mac_container;
- int r, i;
+static int add_veth(
+ sd_netlink *rtnl,
+ pid_t pid,
+ const char *ifname_host,
+ const struct ether_addr *mac_host,
+ const char *ifname_container,
+ const struct ether_addr *mac_container) {
- /* Use two different interface name prefixes depending whether
- * we are in bridge mode or not. */
- snprintf(iface_name, IFNAMSIZ - 1, "%s-%s",
- bridge ? "vb" : "ve", machine_name);
-
- r = generate_mac(machine_name, &mac_container, CONTAINER_HASH_KEY, 0);
- if (r < 0)
- return log_error_errno(r, "Failed to generate predictable MAC address for container side: %m");
-
- r = generate_mac(machine_name, &mac_host, HOST_HASH_KEY, 0);
- if (r < 0)
- return log_error_errno(r, "Failed to generate predictable MAC address for host side: %m");
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+ int r;
- r = sd_netlink_open(&rtnl);
- if (r < 0)
- return log_error_errno(r, "Failed to connect to netlink: %m");
+ assert(rtnl);
+ assert(ifname_host);
+ assert(mac_host);
+ assert(ifname_container);
+ assert(mac_container);
r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0);
if (r < 0)
return log_error_errno(r, "Failed to allocate netlink message: %m");
- r = sd_netlink_message_append_string(m, IFLA_IFNAME, iface_name);
+ r = sd_netlink_message_append_string(m, IFLA_IFNAME, ifname_host);
if (r < 0)
return log_error_errno(r, "Failed to add netlink interface name: %m");
- r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, &mac_host);
+ r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, mac_host);
if (r < 0)
return log_error_errno(r, "Failed to add netlink MAC address: %m");
@@ -134,11 +127,11 @@ int setup_veth(const char *machine_name,
if (r < 0)
return log_error_errno(r, "Failed to open netlink container: %m");
- r = sd_netlink_message_append_string(m, IFLA_IFNAME, "host0");
+ r = sd_netlink_message_append_string(m, IFLA_IFNAME, ifname_container);
if (r < 0)
return log_error_errno(r, "Failed to add netlink interface name: %m");
- r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, &mac_container);
+ r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, mac_container);
if (r < 0)
return log_error_errno(r, "Failed to add netlink MAC address: %m");
@@ -160,7 +153,44 @@ int setup_veth(const char *machine_name,
r = sd_netlink_call(rtnl, m, 0, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to add new veth interfaces (host0, %s): %m", iface_name);
+ return log_error_errno(r, "Failed to add new veth interfaces (%s:%s): %m", ifname_host, ifname_container);
+
+ return 0;
+}
+
+int setup_veth(const char *machine_name,
+ pid_t pid,
+ char iface_name[IFNAMSIZ],
+ bool bridge) {
+
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+ struct ether_addr mac_host, mac_container;
+ int r, i;
+
+ assert(machine_name);
+ assert(pid > 0);
+ assert(iface_name);
+
+ /* Use two different interface name prefixes depending whether
+ * we are in bridge mode or not. */
+ snprintf(iface_name, IFNAMSIZ - 1, "%s-%s",
+ bridge ? "vb" : "ve", machine_name);
+
+ r = generate_mac(machine_name, &mac_container, CONTAINER_HASH_KEY, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate predictable MAC address for container side: %m");
+
+ r = generate_mac(machine_name, &mac_host, HOST_HASH_KEY, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate predictable MAC address for host side: %m");
+
+ r = sd_netlink_open(&rtnl);
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to netlink: %m");
+
+ r = add_veth(rtnl, pid, iface_name, &mac_host, "host0", &mac_container);
+ if (r < 0)
+ return r;
i = (int) if_nametoindex(iface_name);
if (i <= 0)
@@ -169,9 +199,50 @@ int setup_veth(const char *machine_name,
return i;
}
+int setup_veth_extra(
+ const char *machine_name,
+ pid_t pid,
+ char **pairs) {
+
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+ uint64_t idx = 0;
+ char **a, **b;
+ int r;
+
+ assert(machine_name);
+ assert(pid > 0);
+
+ if (strv_isempty(pairs))
+ return 0;
+
+ r = sd_netlink_open(&rtnl);
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to netlink: %m");
+
+ STRV_FOREACH_PAIR(a, b, pairs) {
+ struct ether_addr mac_host, mac_container;
+
+ r = generate_mac(machine_name, &mac_container, VETH_EXTRA_CONTAINER_HASH_KEY, idx);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate predictable MAC address for container side of extra veth link: %m");
+
+ r = generate_mac(machine_name, &mac_host, VETH_EXTRA_HOST_HASH_KEY, idx);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate predictable MAC address for container side of extra veth link: %m");
+
+ r = add_veth(rtnl, pid, *a, &mac_host, *b, &mac_container);
+ if (r < 0)
+ return r;
+
+ idx ++;
+ }
+
+ return 0;
+}
+
int setup_bridge(const char *veth_name, const char *bridge_name) {
- _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
- _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
int r, bridge_ifi;
assert(veth_name);
@@ -232,7 +303,7 @@ static int parse_interface(struct udev *udev, const char *name) {
int move_network_interfaces(pid_t pid, char **ifaces) {
_cleanup_udev_unref_ struct udev *udev = NULL;
- _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
char **i;
int r;
@@ -250,7 +321,7 @@ int move_network_interfaces(pid_t pid, char **ifaces) {
}
STRV_FOREACH(i, ifaces) {
- _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int ifi;
ifi = parse_interface(udev, *i);
@@ -275,7 +346,7 @@ int move_network_interfaces(pid_t pid, char **ifaces) {
int setup_macvlan(const char *machine_name, pid_t pid, char **ifaces) {
_cleanup_udev_unref_ struct udev *udev = NULL;
- _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
unsigned idx = 0;
char **i;
int r;
@@ -294,7 +365,7 @@ int setup_macvlan(const char *machine_name, pid_t pid, char **ifaces) {
}
STRV_FOREACH(i, ifaces) {
- _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
_cleanup_free_ char *n = NULL;
struct ether_addr mac;
int ifi;
@@ -363,7 +434,7 @@ int setup_macvlan(const char *machine_name, pid_t pid, char **ifaces) {
int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces) {
_cleanup_udev_unref_ struct udev *udev = NULL;
- _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
char **i;
int r;
@@ -381,7 +452,7 @@ int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces) {
}
STRV_FOREACH(i, ifaces) {
- _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
_cleanup_free_ char *n = NULL;
int ifi;
@@ -438,3 +509,34 @@ int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces) {
return 0;
}
+
+int veth_extra_parse(char ***l, const char *p) {
+ _cleanup_free_ char *a = NULL, *b = NULL;
+ int r;
+
+ r = extract_first_word(&p, &a, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
+ if (r < 0)
+ return r;
+ if (r == 0 || isempty(a))
+ return -EINVAL;
+
+ r = extract_first_word(&p, &b, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
+ if (r < 0)
+ return r;
+ if (r == 0 || isempty(b)) {
+ free(b);
+ b = strdup(a);
+ if (!b)
+ return -ENOMEM;
+ }
+
+ if (p)
+ return -EINVAL;
+
+ r = strv_push_pair(l, a, b);
+ if (r < 0)
+ return -ENOMEM;
+
+ a = b = NULL;
+ return 0;
+}
diff --git a/src/nspawn/nspawn-network.h b/src/nspawn/nspawn-network.h
index 311e6d06cb..c91fc79c42 100644
--- a/src/nspawn/nspawn-network.h
+++ b/src/nspawn/nspawn-network.h
@@ -22,11 +22,11 @@
***/
#include <net/if.h>
-
-#include <sys/types.h>
#include <stdbool.h>
+#include <sys/types.h>
int setup_veth(const char *machine_name, pid_t pid, char iface_name[IFNAMSIZ], bool bridge);
+int setup_veth_extra(const char *machine_name, pid_t pid, char **pairs);
int setup_bridge(const char *veth_name, const char *bridge_name);
@@ -34,3 +34,5 @@ int setup_macvlan(const char *machine_name, pid_t pid, char **ifaces);
int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces);
int move_network_interfaces(pid_t pid, char **ifaces);
+
+int veth_extra_parse(char ***l, const char *p);
diff --git a/src/nspawn/nspawn-register.c b/src/nspawn/nspawn-register.c
index b2776a61c2..65ca9c762b 100644
--- a/src/nspawn/nspawn-register.c
+++ b/src/nspawn/nspawn-register.c
@@ -21,12 +21,12 @@
#include "sd-bus.h"
-#include "util.h"
-#include "strv.h"
-#include "bus-util.h"
#include "bus-error.h"
-
+#include "bus-util.h"
#include "nspawn-register.h"
+#include "stat-util.h"
+#include "strv.h"
+#include "util.h"
int register_machine(
const char *machine_name,
@@ -39,10 +39,11 @@ int register_machine(
unsigned n_mounts,
int kill_signal,
char **properties,
- bool keep_unit) {
+ bool keep_unit,
+ const char *service) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
r = sd_bus_default_system(&bus);
@@ -61,13 +62,13 @@ int register_machine(
"sayssusai",
machine_name,
SD_BUS_MESSAGE_APPEND_ID128(uuid),
- "nspawn",
+ service,
"container",
(uint32_t) pid,
strempty(directory),
local_ifindex > 0 ? 1 : 0, local_ifindex);
} else {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
char **i;
unsigned j;
@@ -86,7 +87,7 @@ int register_machine(
"sayssusai",
machine_name,
SD_BUS_MESSAGE_APPEND_ID128(uuid),
- "nspawn",
+ service,
"container",
(uint32_t) pid,
strempty(directory),
@@ -194,9 +195,9 @@ int register_machine(
}
int terminate_machine(pid_t pid) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
const char *path;
int r;
diff --git a/src/nspawn/nspawn-register.h b/src/nspawn/nspawn-register.h
index b27841ff59..d3bfd84e5e 100644
--- a/src/nspawn/nspawn-register.h
+++ b/src/nspawn/nspawn-register.h
@@ -27,5 +27,5 @@
#include "nspawn-mount.h"
-int register_machine(const char *machine_name, pid_t pid, const char *directory, sd_id128_t uuid, int local_ifindex, const char *slice, CustomMount *mounts, unsigned n_mounts, int kill_signal, char **properties, bool keep_unit);
+int register_machine(const char *machine_name, pid_t pid, const char *directory, sd_id128_t uuid, int local_ifindex, const char *slice, CustomMount *mounts, unsigned n_mounts, int kill_signal, char **properties, bool keep_unit, const char *service);
int terminate_machine(pid_t pid);
diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c
index 419f5d1c40..d6b64d8d5a 100644
--- a/src/nspawn/nspawn-settings.c
+++ b/src/nspawn/nspawn-settings.c
@@ -19,12 +19,14 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
-#include "conf-parser.h"
-#include "strv.h"
+#include "alloc-util.h"
#include "cap-list.h"
-
+#include "conf-parser.h"
+#include "nspawn-network.h"
#include "nspawn-settings.h"
+#include "process-util.h"
+#include "strv.h"
+#include "util.h"
int settings_load(FILE *f, const char *path, Settings **ret) {
_cleanup_(settings_freep) Settings *s = NULL;
@@ -76,6 +78,7 @@ Settings* settings_free(Settings *s) {
strv_free(s->network_interfaces);
strv_free(s->network_macvlan);
strv_free(s->network_ipvlan);
+ strv_free(s->network_veth_extra);
free(s->network_bridge);
expose_port_free_all(s->expose_ports);
@@ -85,6 +88,27 @@ Settings* settings_free(Settings *s) {
return NULL;
}
+bool settings_private_network(Settings *s) {
+ assert(s);
+
+ return
+ s->private_network > 0 ||
+ s->network_veth > 0 ||
+ s->network_bridge ||
+ s->network_interfaces ||
+ s->network_macvlan ||
+ s->network_ipvlan ||
+ s->network_veth_extra;
+}
+
+bool settings_network_veth(Settings *s) {
+ assert(s);
+
+ return
+ s->network_veth > 0 ||
+ s->network_bridge;
+}
+
DEFINE_CONFIG_PARSE_ENUM(config_parse_volatile_mode, volatile_mode, VolatileMode, "Failed to parse volatile mode");
int config_parse_expose_port(
@@ -152,7 +176,7 @@ int config_parse_capability(
cap = capability_from_name(word);
if (cap < 0) {
- log_syntax(unit, LOG_ERR, filename, line, cap, "Failed to parse capability, ignoring: %s", word);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse capability, ignoring: %s", word);
continue;
}
@@ -248,15 +272,33 @@ int config_parse_tmpfs(
return 0;
}
- if (settings->network_bridge)
- settings->network_veth = true;
+ return 0;
+}
- if (settings->network_interfaces ||
- settings->network_macvlan ||
- settings->network_ipvlan ||
- settings->network_bridge ||
- settings->network_veth)
- settings->private_network = true;
+int config_parse_veth_extra(
+ 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) {
+
+ Settings *settings = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ r = veth_extra_parse(&settings->network_veth_extra, rvalue);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Invalid extra virtual Ethernet link specification %s: %m", rvalue);
+ return 0;
+ }
return 0;
}
diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h
index 4cec40c1b7..10230a5b83 100644
--- a/src/nspawn/nspawn-settings.h
+++ b/src/nspawn/nspawn-settings.h
@@ -24,9 +24,8 @@
#include <stdio.h>
#include "macro.h"
-
-#include "nspawn-mount.h"
#include "nspawn-expose-ports.h"
+#include "nspawn-mount.h"
typedef enum SettingsMask {
SETTING_BOOT = 1 << 0,
@@ -69,12 +68,16 @@ typedef struct Settings {
char **network_interfaces;
char **network_macvlan;
char **network_ipvlan;
+ char **network_veth_extra;
ExposePort *expose_ports;
} Settings;
int settings_load(FILE *f, const char *path, Settings **ret);
Settings* settings_free(Settings *s);
+bool settings_network_veth(Settings *s);
+bool settings_private_network(Settings *s);
+
DEFINE_TRIVIAL_CLEANUP_FUNC(Settings*, settings_free);
const struct ConfigPerfItem* nspawn_gperf_lookup(const char *key, unsigned length);
@@ -85,3 +88,4 @@ int config_parse_expose_port(const char *unit, const char *filename, unsigned li
int config_parse_volatile_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_bind(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_tmpfs(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_veth_extra(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/nspawn/nspawn-setuid.c b/src/nspawn/nspawn-setuid.c
index eda7f62900..014a40b243 100644
--- a/src/nspawn/nspawn-setuid.c
+++ b/src/nspawn/nspawn-setuid.c
@@ -19,16 +19,19 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <grp.h>
#include <sys/types.h>
#include <unistd.h>
-#include <grp.h>
-#include "util.h"
-#include "signal-util.h"
+#include "alloc-util.h"
+#include "fd-util.h"
#include "mkdir.h"
-#include "process-util.h"
-
#include "nspawn-setuid.h"
+#include "process-util.h"
+#include "signal-util.h"
+#include "string-util.h"
+#include "user-util.h"
+#include "util.h"
static int spawn_getent(const char *database, const char *key, pid_t *rpid) {
int pipe_fds[2];
@@ -258,10 +261,10 @@ int change_uid_gid(const char *user, char **_home) {
return log_error_errno(errno, "Failed to set auxiliary groups: %m");
if (setresgid(gid, gid, gid) < 0)
- return log_error_errno(errno, "setregid() failed: %m");
+ return log_error_errno(errno, "setresgid() failed: %m");
if (setresuid(uid, uid, uid) < 0)
- return log_error_errno(errno, "setreuid() failed: %m");
+ return log_error_errno(errno, "setresuid() failed: %m");
if (_home) {
*_home = home;
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 7451c2bf64..d619206dd6 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -46,21 +46,22 @@
#include "sd-daemon.h"
#include "sd-id128.h"
+#include "alloc-util.h"
#include "barrier.h"
#include "base-filesystem.h"
#include "blkid-util.h"
#include "btrfs-util.h"
-#include "build.h"
#include "cap-list.h"
-#include "capability.h"
+#include "capability-util.h"
#include "cgroup-util.h"
#include "copy.h"
#include "dev-setup.h"
#include "env-util.h"
-#include "event-util.h"
+#include "fd-util.h"
#include "fdset.h"
#include "fileio.h"
#include "formats-util.h"
+#include "fs-util.h"
#include "gpt.h"
#include "hostname-util.h"
#include "log.h"
@@ -69,7 +70,16 @@
#include "macro.h"
#include "missing.h"
#include "mkdir.h"
+#include "mount-util.h"
#include "netlink-util.h"
+#include "nspawn-cgroup.h"
+#include "nspawn-expose-ports.h"
+#include "nspawn-mount.h"
+#include "nspawn-network.h"
+#include "nspawn-register.h"
+#include "nspawn-settings.h"
+#include "nspawn-setuid.h"
+#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
#include "ptyfwd.h"
@@ -79,19 +89,17 @@
#include "seccomp-util.h"
#endif
#include "signal-util.h"
+#include "socket-util.h"
+#include "stat-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
#include "strv.h"
#include "terminal-util.h"
#include "udev-util.h"
+#include "umask-util.h"
+#include "user-util.h"
#include "util.h"
-#include "nspawn-settings.h"
-#include "nspawn-mount.h"
-#include "nspawn-network.h"
-#include "nspawn-expose-ports.h"
-#include "nspawn-cgroup.h"
-#include "nspawn-register.h"
-#include "nspawn-setuid.h"
-
typedef enum ContainerStatus {
CONTAINER_TERMINATED,
CONTAINER_REBOOTED
@@ -156,6 +164,7 @@ static char **arg_network_interfaces = NULL;
static char **arg_network_macvlan = NULL;
static char **arg_network_ipvlan = NULL;
static bool arg_network_veth = false;
+static char **arg_network_veth_extra = NULL;
static char *arg_network_bridge = NULL;
static unsigned long arg_personality = PERSONALITY_INVALID;
static char *arg_image = NULL;
@@ -169,6 +178,7 @@ static bool arg_unified_cgroup_hierarchy = false;
static SettingsMask arg_settings_mask = 0;
static int arg_settings_trusted = -1;
static char **arg_parameters = NULL;
+static const char *arg_container_service_name = "systemd-nspawn";
static void help(void) {
printf("%s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n"
@@ -200,10 +210,13 @@ static void help(void) {
" --network-ipvlan=INTERFACE\n"
" Create a ipvlan network interface based on an\n"
" existing network interface to the container\n"
- " -n --network-veth Add a virtual ethernet connection between host\n"
+ " -n --network-veth Add a virtual Ethernet connection between host\n"
" and container\n"
+ " --network-veth-extra=HOSTIF[:CONTAINERIF]\n"
+ " Add an additional virtual Ethernet link between\n"
+ " host and container\n"
" --network-bridge=INTERFACE\n"
- " Add a virtual ethernet connection between host\n"
+ " Add a virtual Ethernet connection between host\n"
" and container and add it to an existing bridge on\n"
" the host\n"
" -p --port=[PROTOCOL:]HOSTPORT[:CONTAINERPORT]\n"
@@ -277,27 +290,6 @@ static int custom_mounts_prepare(void) {
return 0;
}
-static int set_sanitized_path(char **b, const char *path) {
- char *p;
-
- assert(b);
- assert(path);
-
- p = canonicalize_file_name(path);
- if (!p) {
- if (errno != ENOENT)
- return -errno;
-
- p = path_make_absolute_cwd(path);
- if (!p)
- return -ENOMEM;
- }
-
- free(*b);
- *b = path_kill_slashes(p);
- return 0;
-}
-
static int detect_unified_cgroup_hierarchy(void) {
const char *e;
int r;
@@ -345,6 +337,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_NETWORK_MACVLAN,
ARG_NETWORK_IPVLAN,
ARG_NETWORK_BRIDGE,
+ ARG_NETWORK_VETH_EXTRA,
ARG_PERSONALITY,
ARG_VOLATILE,
ARG_TEMPLATE,
@@ -386,6 +379,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "network-macvlan", required_argument, NULL, ARG_NETWORK_MACVLAN },
{ "network-ipvlan", required_argument, NULL, ARG_NETWORK_IPVLAN },
{ "network-veth", no_argument, NULL, 'n' },
+ { "network-veth-extra", required_argument, NULL, ARG_NETWORK_VETH_EXTRA},
{ "network-bridge", required_argument, NULL, ARG_NETWORK_BRIDGE },
{ "personality", required_argument, NULL, ARG_PERSONALITY },
{ "image", required_argument, NULL, 'i' },
@@ -399,6 +393,7 @@ static int parse_argv(int argc, char *argv[]) {
};
int c, r;
+ const char *p, *e;
uint64_t plus = 0, minus = 0;
bool mask_all_settings = false, mask_no_settings = false;
@@ -414,29 +409,24 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case 'D':
- r = set_sanitized_path(&arg_directory, optarg);
+ r = parse_path_argument_and_warn(optarg, false, &arg_directory);
if (r < 0)
- return log_error_errno(r, "Invalid root directory: %m");
-
+ return r;
break;
case ARG_TEMPLATE:
- r = set_sanitized_path(&arg_template, optarg);
+ r = parse_path_argument_and_warn(optarg, false, &arg_template);
if (r < 0)
- return log_error_errno(r, "Invalid template directory: %m");
-
+ return r;
break;
case 'i':
- r = set_sanitized_path(&arg_image, optarg);
+ r = parse_path_argument_and_warn(optarg, false, &arg_image);
if (r < 0)
- return log_error_errno(r, "Invalid image path: %m");
-
+ return r;
break;
case 'x':
@@ -464,6 +454,15 @@ static int parse_argv(int argc, char *argv[]) {
arg_settings_mask |= SETTING_NETWORK;
break;
+ case ARG_NETWORK_VETH_EXTRA:
+ r = veth_extra_parse(&arg_network_veth_extra, optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --network-veth-extra= parameter: %s", optarg);
+
+ arg_private_network = true;
+ arg_settings_mask |= SETTING_NETWORK;
+ break;
+
case ARG_NETWORK_INTERFACE:
if (strv_extend(&arg_network_interfaces, optarg) < 0)
return log_oom();
@@ -541,15 +540,16 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_CAPABILITY:
case ARG_DROP_CAPABILITY: {
- const char *state, *word;
- size_t length;
+ p = optarg;
+ for(;;) {
+ _cleanup_free_ char *t = NULL;
- FOREACH_WORD_SEPARATOR(word, length, optarg, ",", state) {
- _cleanup_free_ char *t;
+ r = extract_first_word(&p, &t, ",", 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse capability %s.", t);
- t = strndup(word, length);
- if (!t)
- return log_oom();
+ if (r == 0)
+ break;
if (streq(t, "all")) {
if (c == ARG_CAPABILITY)
@@ -924,6 +924,10 @@ static int parse_argv(int argc, char *argv[]) {
if (r < 0)
return r;
+ e = getenv("SYSTEMD_NSPAWN_CONTAINER_SERVICE");
+ if (e)
+ arg_container_service_name = e;
+
return 1;
}
@@ -1192,6 +1196,7 @@ static int copy_devnodes(const char *dest) {
static int setup_pts(const char *dest) {
_cleanup_free_ char *options = NULL;
const char *p;
+ int r;
#ifdef HAVE_SELINUX
if (arg_selinux_apifs_context)
@@ -1214,20 +1219,23 @@ static int setup_pts(const char *dest) {
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");
+ r = userns_lchown(p, 0, 0);
+ if (r < 0)
+ return log_error_errno(r, "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");
+ r = userns_lchown(p, 0, 0);
+ if (r < 0)
+ return log_error_errno(r, "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");
+ r = userns_lchown(p, 0, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to chown /dev/pts/ptmx: %m");
return 0;
}
@@ -1291,7 +1299,7 @@ static int setup_kmsg(const char *dest, int kmsg_socket) {
/* Store away the fd in the socket, so that it stays open as
* long as we run the child */
- r = send_one_fd(kmsg_socket, fd);
+ r = send_one_fd(kmsg_socket, fd, 0);
safe_close(fd);
if (r < 0)
@@ -1409,7 +1417,7 @@ static int setup_journal(const char *directory) {
r = userns_mkdir(directory, p, 0755, 0, 0);
if (r < 0)
- log_warning_errno(errno, "Failed to create directory %s: %m", q);
+ log_warning_errno(r, "Failed to create directory %s: %m", q);
return 0;
}
@@ -1423,15 +1431,11 @@ static int setup_journal(const char *directory) {
if (errno == ENOTDIR) {
log_error("%s already exists and is neither a symlink nor a directory", p);
return r;
- } else {
- log_error_errno(errno, "Failed to remove %s: %m", p);
- return -errno;
- }
+ } else
+ return log_error_errno(errno, "Failed to remove %s: %m", p);
}
- } else if (r != -ENOENT) {
- log_error_errno(errno, "readlink(%s) failed: %m", p);
- return r;
- }
+ } else if (r != -ENOENT)
+ return log_error_errno(r, "readlink(%s) failed: %m", p);
if (arg_link_journal == LINK_GUEST) {
@@ -1439,15 +1443,13 @@ static int setup_journal(const char *directory) {
if (arg_link_journal_try) {
log_debug_errno(errno, "Failed to symlink %s to %s, skipping journal setup: %m", q, p);
return 0;
- } else {
- log_error_errno(errno, "Failed to symlink %s to %s: %m", q, p);
- return -errno;
- }
+ } else
+ return log_error_errno(errno, "Failed to symlink %s to %s: %m", q, p);
}
r = userns_mkdir(directory, p, 0755, 0, 0);
if (r < 0)
- log_warning_errno(errno, "Failed to create directory %s: %m", q);
+ log_warning_errno(r, "Failed to create directory %s: %m", q);
return 0;
}
@@ -1459,10 +1461,8 @@ static int setup_journal(const char *directory) {
if (arg_link_journal_try) {
log_debug_errno(errno, "Failed to create %s, skipping journal setup: %m", p);
return 0;
- } else {
- log_error_errno(errno, "Failed to create %s: %m", p);
- return r;
- }
+ } else
+ return log_error_errno(errno, "Failed to create %s: %m", p);
}
} else if (access(p, F_OK) < 0)
@@ -1472,10 +1472,8 @@ static int setup_journal(const char *directory) {
log_warning("%s is not empty, proceeding anyway.", q);
r = userns_mkdir(directory, p, 0755, 0, 0);
- if (r < 0) {
- log_error_errno(errno, "Failed to create %s: %m", q);
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to create %s: %m", q);
if (mount(p, q, NULL, MS_BIND, NULL) < 0)
return log_error_errno(errno, "Failed to bind mount journal from host into guest: %m");
@@ -1484,7 +1482,7 @@ static int setup_journal(const char *directory) {
}
static int drop_capabilities(void) {
- return capability_bounding_set_drop(~arg_retain, false);
+ return capability_bounding_set_drop(arg_retain, false);
}
static int reset_audit_loginuid(void) {
@@ -1616,20 +1614,24 @@ finish:
static int setup_propagate(const char *root) {
const char *p, *q;
+ int r;
(void) mkdir_p("/run/systemd/nspawn/", 0755);
(void) mkdir_p("/run/systemd/nspawn/propagate", 0600);
p = strjoina("/run/systemd/nspawn/propagate/", arg_machine);
(void) mkdir_p(p, 0600);
- if (userns_mkdir(root, "/run/systemd", 0755, 0, 0) < 0)
- return log_error_errno(errno, "Failed to create /run/systemd: %m");
+ r = userns_mkdir(root, "/run/systemd", 0755, 0, 0);
+ if (r < 0)
+ return log_error_errno(r, "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");
+ r = userns_mkdir(root, "/run/systemd/nspawn", 0755, 0, 0);
+ if (r < 0)
+ return log_error_errno(r, "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");
+ r = userns_mkdir(root, "/run/systemd/nspawn/incoming", 0600, 0, 0);
+ if (r < 0)
+ return log_error_errno(r, "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)
@@ -1679,7 +1681,7 @@ static int setup_image(char **device_path, int *loop_nr) {
}
if (!S_ISREG(st.st_mode)) {
- log_error_errno(errno, "%s is not a regular file or block device: %m", arg_image);
+ log_error("%s is not a regular file or block device.", arg_image);
return -EINVAL;
}
@@ -1771,8 +1773,7 @@ static int dissect_image(
if (errno == 0)
return log_oom();
- log_error_errno(errno, "Failed to set device on blkid probe: %m");
- return -errno;
+ return log_error_errno(errno, "Failed to set device on blkid probe: %m");
}
blkid_probe_enable_partitions(b, 1);
@@ -1788,8 +1789,7 @@ static int dissect_image(
} else if (r != 0) {
if (errno == 0)
errno = EIO;
- log_error_errno(errno, "Failed to probe: %m");
- return -errno;
+ return log_error_errno(errno, "Failed to probe: %m");
}
(void) blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
@@ -1912,8 +1912,7 @@ static int dissect_image(
if (!errno)
errno = ENOMEM;
- log_error_errno(errno, "Failed to get partition device of %s: %m", arg_image);
- return -errno;
+ return log_error_errno(errno, "Failed to get partition device of %s: %m", arg_image);
}
qn = udev_device_get_devnum(q);
@@ -2120,8 +2119,7 @@ static int mount_device(const char *what, const char *where, const char *directo
if (!b) {
if (errno == 0)
return log_oom();
- log_error_errno(errno, "Failed to allocate prober for %s: %m", what);
- return -errno;
+ return log_error_errno(errno, "Failed to allocate prober for %s: %m", what);
}
blkid_probe_enable_superblocks(b, 1);
@@ -2135,8 +2133,7 @@ static int mount_device(const char *what, const char *where, const char *directo
} else if (r != 0) {
if (errno == 0)
errno = EIO;
- log_error_errno(errno, "Failed to probe %s: %m", what);
- return -errno;
+ return log_error_errno(errno, "Failed to probe %s: %m", what);
}
errno = 0;
@@ -2282,12 +2279,10 @@ static int wait_for_container(pid_t pid, ContainerStatus *container) {
return r;
}
-static void nop_handler(int sig) {}
-
static int on_orderly_shutdown(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
pid_t pid;
- pid = PTR_TO_UINT32(userdata);
+ pid = PTR_TO_PID(userdata);
if (pid > 0) {
if (kill(pid, arg_kill_signal) >= 0) {
log_info("Trying to halt container. Send SIGTERM again to trigger immediate termination.");
@@ -2327,9 +2322,9 @@ static int determine_names(void) {
}
if (i->type == IMAGE_RAW)
- r = set_sanitized_path(&arg_image, i->path);
+ r = free_and_strdup(&arg_image, i->path);
else
- r = set_sanitized_path(&arg_directory, i->path);
+ r = free_and_strdup(&arg_directory, i->path);
if (r < 0)
return log_error_errno(r, "Invalid image directory: %m");
@@ -2421,10 +2416,10 @@ static int inner_child(
FDSet *fds) {
_cleanup_free_ char *home = NULL;
- unsigned n_env = 2;
+ unsigned n_env = 1;
const char *envp[] = {
"PATH=" DEFAULT_PATH_SPLIT_USR,
- "container=systemd-nspawn", /* LXC sets container=lxc, so follow the scheme here */
+ NULL, /* container */
NULL, /* TERM */
NULL, /* HOME */
NULL, /* USER */
@@ -2455,7 +2450,11 @@ static int inner_child(
}
}
- r = mount_all(NULL, true, arg_uid_shift, arg_uid_range, arg_selinux_apifs_context);
+ r = mount_all(NULL, arg_userns, true, arg_uid_shift, arg_private_network, arg_uid_range, arg_selinux_apifs_context);
+ if (r < 0)
+ return r;
+
+ r = mount_sysfs(NULL);
if (r < 0)
return r;
@@ -2498,8 +2497,9 @@ static int inner_child(
rtnl_socket = safe_close(rtnl_socket);
}
- if (drop_capabilities() < 0)
- return log_error_errno(errno, "drop_capabilities() failed: %m");
+ r = drop_capabilities();
+ if (r < 0)
+ return log_error_errno(r, "drop_capabilities() failed: %m");
setup_hostname();
@@ -2521,6 +2521,9 @@ static int inner_child(
if (r < 0)
return r;
+ /* LXC sets container=lxc, so follow the scheme here */
+ envp[n_env++] = strjoina("container=", arg_container_service_name);
+
envp[n_env] = strv_find_prefix(environ, "TERM=");
if (envp[n_env])
n_env ++;
@@ -2599,8 +2602,9 @@ static int inner_child(
execle("/bin/sh", "-sh", NULL, env_use);
}
+ r = -errno;
(void) log_open();
- return log_error_errno(errno, "execv() failed: %m");
+ return log_error_errno(r, "execv() failed: %m");
}
static int outer_child(
@@ -2706,7 +2710,7 @@ static int outer_child(
return log_error_errno(r, "Failed to make tree read-only: %m");
}
- r = mount_all(directory, false, arg_uid_shift, arg_uid_range, arg_selinux_apifs_context);
+ r = mount_all(directory, arg_userns, false, arg_private_network, arg_uid_shift, arg_uid_range, arg_selinux_apifs_context);
if (r < 0)
return r;
@@ -2841,7 +2845,7 @@ static int load_settings(void) {
p = j;
j = NULL;
- /* By default we trust configuration from /etc and /run */
+ /* By default, we trust configuration from /etc and /run */
if (arg_settings_trusted < 0)
arg_settings_trusted = true;
@@ -2871,7 +2875,7 @@ static int load_settings(void) {
if (!f && errno != ENOENT)
return log_error_errno(errno, "Failed to open %s: %m", p);
- /* By default we do not trust configuration from /var/lib/machines */
+ /* By default, we do not trust configuration from /var/lib/machines */
if (arg_settings_trusted < 0)
arg_settings_trusted = false;
}
@@ -2913,11 +2917,17 @@ static int load_settings(void) {
}
if ((arg_settings_mask & SETTING_CAPABILITY) == 0) {
+ uint64_t plus;
- if (!arg_settings_trusted && settings->capability != 0)
- log_warning("Ignoring Capability= setting, file %s is not trusted.", p);
- else
- arg_retain |= settings->capability;
+ plus = settings->capability;
+ if (settings_private_network(settings))
+ plus |= (1ULL << CAP_NET_ADMIN);
+
+ if (!arg_settings_trusted && plus != 0) {
+ if (settings->capability != 0)
+ log_warning("Ignoring Capability= setting, file %s is not trusted.", p);
+ } else
+ arg_retain |= plus;
arg_retain &= ~settings->drop_capability;
}
@@ -2968,11 +2978,15 @@ static int load_settings(void) {
settings->network_bridge ||
settings->network_interfaces ||
settings->network_macvlan ||
- settings->network_ipvlan)) {
+ settings->network_ipvlan ||
+ settings->network_veth_extra)) {
if (!arg_settings_trusted)
log_warning("Ignoring network settings, file %s is not trusted.", p);
else {
+ arg_network_veth = settings_network_veth(settings);
+ arg_private_network = settings_private_network(settings);
+
strv_free(arg_network_interfaces);
arg_network_interfaces = settings->network_interfaces;
settings->network_interfaces = NULL;
@@ -2985,13 +2999,13 @@ static int load_settings(void) {
arg_network_ipvlan = settings->network_ipvlan;
settings->network_ipvlan = NULL;
+ strv_free(arg_network_veth_extra);
+ arg_network_veth_extra = settings->network_veth_extra;
+ settings->network_veth_extra = NULL;
+
free(arg_network_bridge);
arg_network_bridge = settings->network_bridge;
settings->network_bridge = NULL;
-
- arg_network_veth = settings->network_veth > 0 || settings->network_bridge;
-
- arg_private_network = true; /* all these settings imply private networking */
}
}
@@ -3097,7 +3111,7 @@ int main(int argc, char *argv[]) {
goto finish;
}
- r = btrfs_subvol_snapshot(arg_directory, np, (arg_read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | BTRFS_SNAPSHOT_FALLBACK_COPY | BTRFS_SNAPSHOT_RECURSIVE);
+ r = btrfs_subvol_snapshot(arg_directory, np, (arg_read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | BTRFS_SNAPSHOT_FALLBACK_COPY | BTRFS_SNAPSHOT_RECURSIVE | BTRFS_SNAPSHOT_QUOTA);
if (r < 0) {
log_error_errno(r, "Failed to create snapshot %s from %s: %m", np, arg_directory);
goto finish;
@@ -3121,7 +3135,7 @@ int main(int argc, char *argv[]) {
}
if (arg_template) {
- r = btrfs_subvol_snapshot(arg_template, arg_directory, (arg_read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | BTRFS_SNAPSHOT_FALLBACK_COPY | BTRFS_SNAPSHOT_RECURSIVE);
+ r = btrfs_subvol_snapshot(arg_template, arg_directory, (arg_read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | BTRFS_SNAPSHOT_FALLBACK_COPY | BTRFS_SNAPSHOT_RECURSIVE | BTRFS_SNAPSHOT_QUOTA);
if (r == -EEXIST) {
if (!arg_quiet)
log_info("Directory %s already exists, not populating from template %s.", arg_directory, arg_template);
@@ -3144,10 +3158,9 @@ int main(int argc, char *argv[]) {
} else {
const char *p;
- p = strjoina(arg_directory,
- argc > optind && path_is_absolute(argv[optind]) ? argv[optind] : "/usr/bin/");
- if (access(p, F_OK) < 0) {
- log_error("Directory %s lacks the binary to execute or doesn't look like a binary tree. Refusing.", arg_directory);
+ p = strjoina(arg_directory, "/usr/");
+ if (laccess(p, F_OK) < 0) {
+ log_error("Directory %s doesn't look like it has an OS tree. Refusing.", arg_directory);
r = -EINVAL;
goto finish;
}
@@ -3236,19 +3249,18 @@ int main(int argc, char *argv[]) {
}
for (;;) {
- _cleanup_close_pair_ int kmsg_socket_pair[2] = { -1, -1 }, rtnl_socket_pair[2] = { -1, -1 }, pid_socket_pair[2] = { -1, -1 },
- uid_shift_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 }, uid_shift_socket_pair[2] = { -1, -1 };
ContainerStatus container_status;
_cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL;
static const struct sigaction sa = {
- .sa_handler = nop_handler,
+ .sa_handler = nop_signal_handler,
.sa_flags = SA_NOCLDSTOP,
};
int ifi = 0;
ssize_t l;
- _cleanup_event_unref_ sd_event *event = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
_cleanup_(pty_forward_freep) PTYForward *forward = NULL;
- _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
char last_char = 0;
r = barrier_create(&barrier);
@@ -3338,8 +3350,7 @@ int main(int argc, char *argv[]) {
barrier_set_role(&barrier, BARRIER_PARENT);
- fdset_free(fds);
- fds = NULL;
+ fds = fdset_free(fds);
kmsg_socket_pair[1] = safe_close(kmsg_socket_pair[1]);
rtnl_socket_pair[1] = safe_close(rtnl_socket_pair[1]);
@@ -3417,6 +3428,10 @@ int main(int argc, char *argv[]) {
}
}
+ r = setup_veth_extra(arg_machine, pid, arg_network_veth_extra);
+ if (r < 0)
+ goto finish;
+
r = setup_macvlan(arg_machine, pid, arg_network_macvlan);
if (r < 0)
goto finish;
@@ -3437,7 +3452,8 @@ int main(int argc, char *argv[]) {
arg_custom_mounts, arg_n_custom_mounts,
arg_kill_signal,
arg_property,
- arg_keep_unit);
+ arg_keep_unit,
+ arg_container_service_name);
if (r < 0)
goto finish;
}
@@ -3493,8 +3509,8 @@ int main(int argc, char *argv[]) {
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));
+ sd_event_add_signal(event, NULL, SIGINT, on_orderly_shutdown, PID_TO_PTR(pid));
+ sd_event_add_signal(event, NULL, SIGTERM, on_orderly_shutdown, PID_TO_PTR(pid));
} else {
/* Immediately exit */
sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
@@ -3514,7 +3530,7 @@ int main(int argc, char *argv[]) {
rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]);
- r = pty_forward_new(event, master, true, !interactive, &forward);
+ r = pty_forward_new(event, master, PTY_FORWARD_IGNORE_VHANGUP | (interactive ? 0 : PTY_FORWARD_READ_ONLY), &forward);
if (r < 0) {
log_error_errno(r, "Failed to create PTY forwarder: %m");
goto finish;
@@ -3593,7 +3609,7 @@ finish:
if (remove_subvol && arg_directory) {
int k;
- k = btrfs_subvol_remove(arg_directory, true);
+ k = btrfs_subvol_remove(arg_directory, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA);
if (k < 0)
log_warning_errno(k, "Cannot remove subvolume '%s', ignoring: %m", arg_directory);
}
@@ -3617,6 +3633,7 @@ finish:
strv_free(arg_network_interfaces);
strv_free(arg_network_macvlan);
strv_free(arg_network_ipvlan);
+ strv_free(arg_network_veth_extra);
strv_free(arg_parameters);
custom_mount_free_all(arg_custom_mounts, arg_n_custom_mounts);
expose_port_free_all(arg_expose_ports);
diff --git a/src/nss-myhostname/nss-myhostname.c b/src/nss-myhostname/nss-myhostname.c
index 0dca891447..ee10b105ea 100644
--- a/src/nss-myhostname/nss-myhostname.c
+++ b/src/nss-myhostname/nss-myhostname.c
@@ -19,17 +19,19 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <nss.h>
-#include <netdb.h>
#include <errno.h>
-#include <string.h>
#include <net/if.h>
+#include <netdb.h>
+#include <nss.h>
#include <stdlib.h>
+#include <string.h>
+#include "alloc-util.h"
+#include "hostname-util.h"
#include "local-addresses.h"
#include "macro.h"
#include "nss-util.h"
-#include "hostname-util.h"
+#include "string-util.h"
#include "util.h"
/* We use 127.0.0.2 as IPv4 address. This has the advantage over
diff --git a/src/nss-mymachines/nss-mymachines.c b/src/nss-mymachines/nss-mymachines.c
index 604130ed25..dcdbc31a78 100644
--- a/src/nss-mymachines/nss-mymachines.c
+++ b/src/nss-mymachines/nss-mymachines.c
@@ -19,18 +19,21 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <nss.h>
#include <netdb.h>
+#include <nss.h>
#include "sd-bus.h"
#include "sd-login.h"
-#include "macro.h"
-#include "util.h"
-#include "nss-util.h"
-#include "bus-util.h"
+
+#include "alloc-util.h"
#include "bus-common-errors.h"
-#include "in-addr-util.h"
#include "hostname-util.h"
+#include "in-addr-util.h"
+#include "macro.h"
+#include "nss-util.h"
+#include "string-util.h"
+#include "user-util.h"
+#include "util.h"
NSS_GETHOSTBYNAME_PROTOTYPES(mymachines);
NSS_GETPW_PROTOTYPES(mymachines);
@@ -82,8 +85,8 @@ enum nss_status _nss_mymachines_gethostbyname4_r(
int32_t *ttlp) {
struct gaih_addrtuple *r_tuple, *r_tuple_first = NULL;
- _cleanup_bus_message_unref_ sd_bus_message* reply = NULL;
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_free_ int *ifindices = NULL;
_cleanup_free_ char *class = NULL;
size_t l, ms, idx;
@@ -231,8 +234,8 @@ enum nss_status _nss_mymachines_gethostbyname3_r(
int32_t *ttlp,
char **canonp) {
- _cleanup_bus_message_unref_ sd_bus_message* reply = NULL;
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_free_ char *class = NULL;
unsigned c = 0, i = 0;
char *r_name, *r_aliases, *r_addr, *r_addr_list;
@@ -392,9 +395,9 @@ enum nss_status _nss_mymachines_getpwnam_r(
char *buffer, size_t buflen,
int *errnop) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message* reply = NULL;
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
const char *p, *e, *machine;
uint32_t mapped;
uid_t uid;
@@ -412,6 +415,9 @@ enum nss_status _nss_mymachines_getpwnam_r(
if (!e || e == p)
goto not_found;
+ if (e - p > HOST_NAME_MAX - 1) /* -1 for the last dash */
+ goto not_found;
+
r = parse_uid(e + 1, &uid);
if (r < 0)
goto not_found;
@@ -478,9 +484,9 @@ enum nss_status _nss_mymachines_getpwuid_r(
char *buffer, size_t buflen,
int *errnop) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message* reply = NULL;
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
const char *machine, *object;
uint32_t mapped;
int r;
@@ -549,9 +555,9 @@ enum nss_status _nss_mymachines_getgrnam_r(
char *buffer, size_t buflen,
int *errnop) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message* reply = NULL;
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
const char *p, *e, *machine;
uint32_t mapped;
uid_t gid;
@@ -569,6 +575,9 @@ enum nss_status _nss_mymachines_getgrnam_r(
if (!e || e == p)
goto not_found;
+ if (e - p > HOST_NAME_MAX - 1) /* -1 for the last dash */
+ goto not_found;
+
r = parse_gid(e + 1, &gid);
if (r < 0)
goto not_found;
@@ -633,9 +642,9 @@ enum nss_status _nss_mymachines_getgrgid_r(
char *buffer, size_t buflen,
int *errnop) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message* reply = NULL;
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
const char *machine, *object;
uint32_t mapped;
int r;
diff --git a/src/nss-resolve/nss-resolve.c b/src/nss-resolve/nss-resolve.c
index ef5eb7b4cf..a268c3ac31 100644
--- a/src/nss-resolve/nss-resolve.c
+++ b/src/nss-resolve/nss-resolve.c
@@ -19,20 +19,21 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <nss.h>
-#include <netdb.h>
+#include <dlfcn.h>
#include <errno.h>
-#include <string.h>
+#include <netdb.h>
+#include <nss.h>
#include <stdlib.h>
-#include <dlfcn.h>
+#include <string.h>
#include "sd-bus.h"
-#include "bus-util.h"
+
#include "bus-common-errors.h"
+#include "in-addr-util.h"
#include "macro.h"
#include "nss-util.h"
+#include "string-util.h"
#include "util.h"
-#include "in-addr-util.h"
NSS_GETHOSTBYNAME_PROTOTYPES(resolve);
NSS_GETHOSTBYADDR_PROTOTYPES(resolve);
@@ -117,10 +118,10 @@ enum nss_status _nss_resolve_gethostbyname4_r(
int *errnop, int *h_errnop,
int32_t *ttlp) {
- _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
struct gaih_addrtuple *r_tuple, *r_tuple_first = NULL;
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
const char *canonical = NULL;
size_t l, ms, idx;
char *r_name;
@@ -298,10 +299,10 @@ enum nss_status _nss_resolve_gethostbyname3_r(
int32_t *ttlp,
char **canonp) {
- _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
char *r_name, *r_aliases, *r_addr, *r_addr_list;
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
size_t l, idx, ms, alen;
const char *canonical;
int c, r, i = 0;
@@ -502,10 +503,10 @@ enum nss_status _nss_resolve_gethostbyaddr2_r(
int *errnop, int *h_errnop,
int32_t *ttlp) {
- _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
char *r_name, *r_aliases, *r_addr, *r_addr_list;
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
unsigned c = 0, i = 0;
size_t ms = 0, idx;
const char *n;
diff --git a/src/path/path.c b/src/path/path.c
index f7736a4202..0ece72f6fe 100644
--- a/src/path/path.c
+++ b/src/path/path.c
@@ -19,16 +19,18 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
-#include <getopt.h>
#include <errno.h>
+#include <getopt.h>
+#include <stdio.h>
#include <stdlib.h>
#include "sd-path.h"
-#include "build.h"
+
+#include "alloc-util.h"
+#include "log.h"
#include "macro.h"
+#include "string-util.h"
#include "util.h"
-#include "log.h"
static const char *arg_suffix = NULL;
@@ -155,9 +157,7 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case ARG_SUFFIX:
arg_suffix = optarg;
diff --git a/src/quotacheck/quotacheck.c b/src/quotacheck/quotacheck.c
index cf6a239402..883d96608d 100644
--- a/src/quotacheck/quotacheck.c
+++ b/src/quotacheck/quotacheck.c
@@ -19,15 +19,17 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
-#include <stdbool.h>
#include <errno.h>
-#include <unistd.h>
+#include <stdbool.h>
+#include <stdio.h>
#include <sys/prctl.h>
+#include <unistd.h>
-#include "util.h"
+#include "proc-cmdline.h"
#include "process-util.h"
#include "signal-util.h"
+#include "string-util.h"
+#include "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 f4778fc16a..d857ade36a 100644
--- a/src/random-seed/random-seed.c
+++ b/src/random-seed/random-seed.c
@@ -19,15 +19,19 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
-#include <fcntl.h>
#include <errno.h>
+#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
+#include <unistd.h>
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "io-util.h"
#include "log.h"
-#include "util.h"
#include "mkdir.h"
+#include "string-util.h"
+#include "util.h"
#define POOL_SIZE_MIN 512
diff --git a/src/rc-local-generator/rc-local-generator.c b/src/rc-local-generator/rc-local-generator.c
index d4e6ba4bf9..6ecadbf3e5 100644
--- a/src/rc-local-generator/rc-local-generator.c
+++ b/src/rc-local-generator/rc-local-generator.c
@@ -24,9 +24,11 @@
#include <stdio.h>
#include <unistd.h>
+#include "alloc-util.h"
#include "log.h"
-#include "util.h"
#include "mkdir.h"
+#include "string-util.h"
+#include "util.h"
#ifndef RC_LOCAL_SCRIPT_PATH_START
#define RC_LOCAL_SCRIPT_PATH_START "/etc/rc.d/rc.local"
@@ -60,8 +62,7 @@ static int add_symlink(const char *service, const char *where) {
if (errno == EEXIST)
return 0;
- 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 1;
diff --git a/src/remount-fs/remount-fs.c b/src/remount-fs/remount-fs.c
index f904e48e75..9fc56284d2 100644
--- a/src/remount-fs/remount-fs.c
+++ b/src/remount-fs/remount-fs.c
@@ -19,29 +19,33 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
#include <errno.h>
+#include <mntent.h>
#include <string.h>
+#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/wait.h>
-#include <mntent.h>
+#include <unistd.h>
+#include "exit-status.h"
#include "log.h"
-#include "util.h"
+#include "mount-setup.h"
+#include "mount-util.h"
#include "path-util.h"
+#include "process-util.h"
#include "signal-util.h"
-#include "mount-setup.h"
-#include "exit-status.h"
+#include "strv.h"
+#include "util.h"
/* Goes through /etc/fstab and remounts all API file systems, applying
* options that are in /etc/fstab that systemd might not have
* respected */
int main(int argc, char *argv[]) {
- int ret = EXIT_FAILURE;
+ _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
_cleanup_endmntent_ FILE *f = NULL;
struct mntent* me;
- Hashmap *pids = NULL;
+ int r;
if (argc > 1) {
log_error("This program takes no argument.");
@@ -56,21 +60,21 @@ int main(int argc, char *argv[]) {
f = setmntent("/etc/fstab", "r");
if (!f) {
- if (errno == ENOENT)
- return EXIT_SUCCESS;
+ if (errno == ENOENT) {
+ r = 0;
+ goto finish;
+ }
- log_error_errno(errno, "Failed to open /etc/fstab: %m");
- return EXIT_FAILURE;
+ r = log_error_errno(errno, "Failed to open /etc/fstab: %m");
+ goto finish;
}
pids = hashmap_new(NULL);
if (!pids) {
- log_error("Failed to allocate set");
+ r = log_oom();
goto finish;
}
- ret = EXIT_SUCCESS;
-
while ((me = getmntent(f))) {
pid_t pid;
int k;
@@ -86,25 +90,18 @@ int main(int argc, char *argv[]) {
pid = fork();
if (pid < 0) {
- log_error_errno(errno, "Failed to fork: %m");
- ret = EXIT_FAILURE;
- continue;
+ r = log_error_errno(errno, "Failed to fork: %m");
+ goto finish;
}
if (pid == 0) {
- const char *arguments[5];
/* Child */
(void) reset_all_signal_handlers();
(void) reset_signal_mask();
+ (void) prctl(PR_SET_PDEATHSIG, SIGTERM);
- arguments[0] = MOUNT_PATH;
- arguments[1] = me->mnt_dir;
- arguments[2] = "-o";
- arguments[3] = "remount";
- arguments[4] = NULL;
-
- execv(MOUNT_PATH, (char **) arguments);
+ execv(MOUNT_PATH, STRV_MAKE(MOUNT_PATH, me->mnt_dir, "-o", "remount"));
log_error_errno(errno, "Failed to execute " MOUNT_PATH ": %m");
_exit(EXIT_FAILURE);
@@ -114,20 +111,19 @@ int main(int argc, char *argv[]) {
s = strdup(me->mnt_dir);
if (!s) {
- log_oom();
- ret = EXIT_FAILURE;
- continue;
+ r = log_oom();
+ goto finish;
}
-
- k = hashmap_put(pids, UINT_TO_PTR(pid), s);
+ k = hashmap_put(pids, PID_TO_PTR(pid), s);
if (k < 0) {
- log_error_errno(k, "Failed to add PID to set: %m");
- ret = EXIT_FAILURE;
- continue;
+ free(s);
+ r = log_oom();
+ goto finish;
}
}
+ r = 0;
while (!hashmap_isempty(pids)) {
siginfo_t si = {};
char *s;
@@ -137,12 +133,11 @@ int main(int argc, char *argv[]) {
if (errno == EINTR)
continue;
- log_error_errno(errno, "waitid() failed: %m");
- ret = EXIT_FAILURE;
- break;
+ r = log_error_errno(errno, "waitid() failed: %m");
+ goto finish;
}
- s = hashmap_remove(pids, UINT_TO_PTR(si.si_pid));
+ s = hashmap_remove(pids, PID_TO_PTR(si.si_pid));
if (s) {
if (!is_clean_exit(si.si_code, si.si_status, NULL)) {
if (si.si_code == CLD_EXITED)
@@ -150,7 +145,7 @@ int main(int argc, char *argv[]) {
else
log_error(MOUNT_PATH " for %s terminated by signal %s.", s, signal_to_string(si.si_status));
- ret = EXIT_FAILURE;
+ r = -ENOEXEC;
}
free(s);
@@ -158,9 +153,5 @@ int main(int argc, char *argv[]) {
}
finish:
-
- if (pids)
- hashmap_free_free(pids);
-
- return ret;
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/reply-password/reply-password.c b/src/reply-password/reply-password.c
index d0d61b98ed..166ab470ed 100644
--- a/src/reply-password/reply-password.c
+++ b/src/reply-password/reply-password.c
@@ -19,14 +19,16 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/socket.h>
-#include <string.h>
#include <errno.h>
-#include <sys/un.h>
#include <stddef.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include "fd-util.h"
#include "log.h"
#include "macro.h"
+#include "string-util.h"
#include "util.h"
static int send_on_socket(int fd, const char *socket_name, const void *packet, size_t size) {
@@ -50,9 +52,10 @@ static int send_on_socket(int fd, const char *socket_name, const void *packet, s
}
int main(int argc, char *argv[]) {
- int fd = -1, r = EXIT_FAILURE;
+ _cleanup_close_ int fd = -1;
char packet[LINE_MAX];
size_t length;
+ int r;
log_set_target(LOG_TARGET_AUTO);
log_parse_environment();
@@ -60,14 +63,14 @@ int main(int argc, char *argv[]) {
if (argc != 3) {
log_error("Wrong number of arguments.");
- goto finish;
+ return EXIT_FAILURE;
}
if (streq(argv[1], "1")) {
packet[0] = '+';
if (!fgets(packet+1, sizeof(packet)-1, stdin)) {
- log_error_errno(errno, "Failed to read password: %m");
+ r = log_error_errno(errno, "Failed to read password: %m");
goto finish;
}
@@ -78,22 +81,20 @@ int main(int argc, char *argv[]) {
length = 1;
} else {
log_error("Invalid first argument %s", argv[1]);
+ r = -EINVAL;
goto finish;
}
fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (fd < 0) {
- log_error_errno(errno, "socket() failed: %m");
+ r = log_error_errno(errno, "socket() failed: %m");
goto finish;
}
- if (send_on_socket(fd, argv[2], packet, length) < 0)
- goto finish;
-
- r = EXIT_SUCCESS;
+ r = send_on_socket(fd, argv[2], packet, length);
finish:
- safe_close(fd);
+ memory_erase(packet, sizeof(packet));
- return r;
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/resolve-host/resolve-host.c b/src/resolve-host/resolve-host.c
index 22af092cc0..54a060ea5a 100644
--- a/src/resolve-host/resolve-host.c
+++ b/src/resolve-host/resolve-host.c
@@ -19,28 +19,39 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <net/if.h>
#include <getopt.h>
+#include <net/if.h>
#include "sd-bus.h"
-#include "bus-util.h"
+
+#include "af-list.h"
+#include "alloc-util.h"
#include "bus-error.h"
+#include "bus-util.h"
+#include "escape.h"
#include "in-addr-util.h"
-#include "af-list.h"
-#include "build.h"
-
-#include "resolved-dns-packet.h"
+#include "parse-util.h"
#include "resolved-def.h"
+#include "resolved-dns-packet.h"
+#include "terminal-util.h"
#define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
static int arg_family = AF_UNSPEC;
static int arg_ifindex = 0;
-static int arg_type = 0;
+static uint16_t arg_type = 0;
static uint16_t arg_class = 0;
static bool arg_legend = true;
static uint64_t arg_flags = 0;
+static enum {
+ MODE_RESOLVE_HOST,
+ MODE_RESOLVE_RECORD,
+ MODE_RESOLVE_SERVICE,
+ MODE_STATISTICS,
+ MODE_RESET_STATISTICS,
+} arg_mode = MODE_RESOLVE_HOST;
+
static void print_source(uint64_t flags, usec_t rtt) {
char rtt_str[FORMAT_TIMESTAMP_MAX];
@@ -53,10 +64,12 @@ static void print_source(uint64_t flags, usec_t rtt) {
fputs("\n-- Information acquired via", stdout);
if (flags != 0)
- printf(" protocol%s%s%s",
+ printf(" protocol%s%s%s%s%s",
flags & SD_RESOLVED_DNS ? " DNS" :"",
flags & SD_RESOLVED_LLMNR_IPV4 ? " LLMNR/IPv4" : "",
- flags & SD_RESOLVED_LLMNR_IPV6 ? " LLMNR/IPv6" : "");
+ flags & SD_RESOLVED_LLMNR_IPV6 ? " LLMNR/IPv6" : "",
+ flags & SD_RESOLVED_MDNS_IPV4 ? "mDNS/IPv4" : "",
+ flags & SD_RESOLVED_MDNS_IPV6 ? "mDNS/IPv6" : "");
assert_se(format_timespan(rtt_str, sizeof(rtt_str), rtt, 100));
@@ -64,12 +77,14 @@ static void print_source(uint64_t flags, usec_t rtt) {
fputc('.', stdout);
fputc('\n', stdout);
+
+ printf("-- Data is authenticated: %s\n", yes_no(flags & SD_RESOLVED_AUTHENTICATED));
}
static int resolve_host(sd_bus *bus, const char *name) {
- _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
const char *canonical = NULL;
char ifname[IF_NAMESIZE] = "";
unsigned c = 0;
@@ -101,10 +116,8 @@ static int resolve_host(sd_bus *bus, const char *name) {
ts = now(CLOCK_MONOTONIC);
r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
- if (r < 0) {
- log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "%s: resolve call failed: %s", name, bus_error_message(&error, r));
ts = now(CLOCK_MONOTONIC) - ts;
@@ -113,10 +126,10 @@ static int resolve_host(sd_bus *bus, const char *name) {
return bus_log_parse_error(r);
while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
- const void *a;
- size_t sz;
_cleanup_free_ char *pretty = NULL;
int ifindex, family;
+ const void *a;
+ size_t sz;
assert_cc(sizeof(int) == sizeof(int32_t));
@@ -139,7 +152,7 @@ static int resolve_host(sd_bus *bus, const char *name) {
if (sz != FAMILY_ADDRESS_SIZE(family)) {
log_error("%s: systemd-resolved returned address of invalid size %zu for family %s", name, sz, af_to_name(family) ?: "unknown");
- continue;
+ return -EINVAL;
}
ifname[0] = 0;
@@ -184,8 +197,8 @@ static int resolve_host(sd_bus *bus, const char *name) {
}
static int resolve_address(sd_bus *bus, int family, const union in_addr_union *address, int ifindex) {
- _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_free_ char *pretty = NULL;
char ifname[IF_NAMESIZE] = "";
uint64_t flags;
@@ -299,8 +312,7 @@ static int parse_address(const char *s, int *family, union in_addr_union *addres
percent = strchr(s, '%');
if (percent) {
- r = safe_atoi(percent+1, &ifi);
- if (r < 0 || ifi <= 0) {
+ if (parse_ifindex(percent+1, &ifi) < 0) {
ifi = if_nametoindex(percent+1);
if (ifi <= 0)
return -EINVAL;
@@ -318,10 +330,9 @@ static int parse_address(const char *s, int *family, union in_addr_union *addres
return 0;
}
-static int resolve_record(sd_bus *bus, const char *name) {
-
- _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_t type) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
char ifname[IF_NAMESIZE] = "";
unsigned n = 0;
uint64_t flags;
@@ -333,7 +344,7 @@ static int resolve_record(sd_bus *bus, const char *name) {
if (arg_ifindex > 0 && !if_indextoname(arg_ifindex, ifname))
return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", arg_ifindex);
- log_debug("Resolving %s %s %s (interface %s).", name, dns_class_to_string(arg_class), dns_type_to_string(arg_type), isempty(ifname) ? "*" : ifname);
+ log_debug("Resolving %s %s %s (interface %s).", name, dns_class_to_string(class), dns_type_to_string(type), isempty(ifname) ? "*" : ifname);
r = sd_bus_message_new_method_call(
bus,
@@ -345,8 +356,7 @@ static int resolve_record(sd_bus *bus, const char *name) {
if (r < 0)
return bus_log_create_error(r);
- assert((uint16_t) arg_type == arg_type);
- r = sd_bus_message_append(req, "isqqt", arg_ifindex, name, arg_class, arg_type, arg_flags);
+ r = sd_bus_message_append(req, "isqqt", arg_ifindex, name, class, type, arg_flags);
if (r < 0)
return bus_log_create_error(r);
@@ -367,7 +377,7 @@ static int resolve_record(sd_bus *bus, const char *name) {
while ((r = sd_bus_message_enter_container(reply, 'r', "iqqay")) > 0) {
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
- _cleanup_free_ char *s = NULL;
+ const char *s;
uint16_t c, t;
int ifindex;
const void *d;
@@ -397,17 +407,13 @@ static int resolve_record(sd_bus *bus, const char *name) {
if (r < 0)
return log_oom();
- r = dns_packet_read_rr(p, &rr, NULL);
- if (r < 0) {
- log_error("Failed to parse RR.");
- return r;
- }
+ r = dns_packet_read_rr(p, &rr, NULL, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse RR: %m");
- r = dns_resource_record_to_string(rr, &s);
- if (r < 0) {
- log_error("Failed to format RR.");
- return r;
- }
+ s = dns_resource_record_to_string(rr);
+ if (!s)
+ return log_oom();
ifname[0] = 0;
if (ifindex > 0 && !if_indextoname(ifindex, ifname))
@@ -437,6 +443,463 @@ static int resolve_record(sd_bus *bus, const char *name) {
return 0;
}
+static int resolve_rfc4501(sd_bus *bus, const char *name) {
+ uint16_t type = 0, class = 0;
+ const char *p, *q, *n;
+ int r;
+
+ assert(bus);
+ assert(name);
+ assert(startswith(name, "dns:"));
+
+ /* Parse RFC 4501 dns: URIs */
+
+ p = name + 4;
+
+ if (p[0] == '/') {
+ const char *e;
+
+ if (p[1] != '/')
+ goto invalid;
+
+ e = strchr(p + 2, '/');
+ if (!e)
+ goto invalid;
+
+ if (e != p + 2)
+ log_warning("DNS authority specification not supported; ignoring specified authority.");
+
+ p = e + 1;
+ }
+
+ q = strchr(p, '?');
+ if (q) {
+ n = strndupa(p, q - p);
+ q++;
+
+ for (;;) {
+ const char *f;
+
+ f = startswith_no_case(q, "class=");
+ if (f) {
+ _cleanup_free_ char *t = NULL;
+ const char *e;
+
+ if (class != 0) {
+ log_error("DNS class specified twice.");
+ return -EINVAL;
+ }
+
+ e = strchrnul(f, ';');
+ t = strndup(f, e - f);
+ if (!t)
+ return log_oom();
+
+ r = dns_class_from_string(t);
+ if (r < 0) {
+ log_error("Unknown DNS class %s.", t);
+ return -EINVAL;
+ }
+
+ class = r;
+
+ if (*e == ';') {
+ q = e + 1;
+ continue;
+ }
+
+ break;
+ }
+
+ f = startswith_no_case(q, "type=");
+ if (f) {
+ _cleanup_free_ char *t = NULL;
+ const char *e;
+
+ if (type != 0) {
+ log_error("DNS type specified twice.");
+ return -EINVAL;
+ }
+
+ e = strchrnul(f, ';');
+ t = strndup(f, e - f);
+ if (!t)
+ return log_oom();
+
+ r = dns_type_from_string(t);
+ if (r < 0) {
+ log_error("Unknown DNS type %s.", t);
+ return -EINVAL;
+ }
+
+ type = r;
+
+ if (*e == ';') {
+ q = e + 1;
+ continue;
+ }
+
+ break;
+ }
+
+ goto invalid;
+ }
+ } else
+ n = p;
+
+ if (type == 0)
+ type = arg_type;
+ if (type == 0)
+ type = DNS_TYPE_A;
+
+ if (class == 0)
+ class = arg_class;
+ if (class == 0)
+ class = DNS_CLASS_IN;
+
+ return resolve_record(bus, n, class, type);
+
+invalid:
+ log_error("Invalid DNS URI: %s", name);
+ return -EINVAL;
+}
+
+static int resolve_service(sd_bus *bus, const char *name, const char *type, const char *domain) {
+ const char *canonical_name, *canonical_type, *canonical_domain;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ char ifname[IF_NAMESIZE] = "";
+ size_t indent, sz;
+ uint64_t flags;
+ const char *p;
+ unsigned c;
+ usec_t ts;
+ int r;
+
+ assert(bus);
+ assert(domain);
+
+ if (isempty(name))
+ name = NULL;
+ if (isempty(type))
+ type = NULL;
+
+ if (arg_ifindex > 0 && !if_indextoname(arg_ifindex, ifname))
+ return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", arg_ifindex);
+
+ if (name)
+ log_debug("Resolving service \"%s\" of type %s in %s (family %s, interface %s).", name, type, domain, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname);
+ else if (type)
+ log_debug("Resolving service type %s of %s (family %s, interface %s).", type, domain, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname);
+ else
+ log_debug("Resolving service type %s (family %s, interface %s).", domain, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname);
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &req,
+ "org.freedesktop.resolve1",
+ "/org/freedesktop/resolve1",
+ "org.freedesktop.resolve1.Manager",
+ "ResolveService");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(req, "isssit", arg_ifindex, name, type, domain, arg_family, arg_flags);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ ts = now(CLOCK_MONOTONIC);
+
+ r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
+ if (r < 0)
+ return log_error_errno(r, "Resolve call failed: %s", bus_error_message(&error, r));
+
+ ts = now(CLOCK_MONOTONIC) - ts;
+
+ r = sd_bus_message_enter_container(reply, 'a', "(qqqsa(iiay)s)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ indent =
+ (name ? strlen(name) + 1 : 0) +
+ (type ? strlen(type) + 1 : 0) +
+ strlen(domain) + 2;
+
+ c = 0;
+ while ((r = sd_bus_message_enter_container(reply, 'r', "qqqsa(iiay)s")) > 0) {
+ uint16_t priority, weight, port;
+ const char *hostname, *canonical;
+
+ r = sd_bus_message_read(reply, "qqqs", &priority, &weight, &port, &hostname);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (name)
+ printf("%*s%s", (int) strlen(name), c == 0 ? name : "", c == 0 ? "/" : " ");
+ if (type)
+ printf("%*s%s", (int) strlen(type), c == 0 ? type : "", c == 0 ? "/" : " ");
+
+ printf("%*s%s %s:%u [priority=%u, weight=%u]\n",
+ (int) strlen(domain), c == 0 ? domain : "",
+ c == 0 ? ":" : " ",
+ hostname, port,
+ priority, weight);
+
+ r = sd_bus_message_enter_container(reply, 'a', "(iiay)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
+ _cleanup_free_ char *pretty = NULL;
+ int ifindex, family;
+ const void *a;
+
+ assert_cc(sizeof(int) == sizeof(int32_t));
+
+ r = sd_bus_message_read(reply, "ii", &ifindex, &family);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_read_array(reply, 'y', &a, &sz);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (!IN_SET(family, AF_INET, AF_INET6)) {
+ log_debug("%s: skipping entry with family %d (%s)", name, family, af_to_name(family) ?: "unknown");
+ continue;
+ }
+
+ if (sz != FAMILY_ADDRESS_SIZE(family)) {
+ log_error("%s: systemd-resolved returned address of invalid size %zu for family %s", name, sz, af_to_name(family) ?: "unknown");
+ return -EINVAL;
+ }
+
+ ifname[0] = 0;
+ if (ifindex > 0 && !if_indextoname(ifindex, ifname))
+ log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
+
+ r = in_addr_to_string(family, a, &pretty);
+ if (r < 0)
+ return log_error_errno(r, "Failed to print address for %s: %m", name);
+
+ printf("%*s%s%s%s\n", (int) indent, "", pretty, isempty(ifname) ? "" : "%s", ifname);
+ }
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_read(reply, "s", &canonical);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (!streq(hostname, canonical))
+ printf("%*s(%s)\n", (int) indent, "", canonical);
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ c++;
+ }
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_enter_container(reply, 'a', "ay");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ c = 0;
+ while ((r = sd_bus_message_read_array(reply, 'y', (const void**) &p, &sz)) > 0) {
+ _cleanup_free_ char *escaped = NULL;
+
+ escaped = cescape_length(p, sz);
+ if (!escaped)
+ return log_oom();
+
+ printf("%*s%s\n", (int) indent, "", escaped);
+ c++;
+ }
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_read(reply, "ssst", &canonical_name, &canonical_type, &canonical_domain, &flags);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (isempty(canonical_name))
+ canonical_name = NULL;
+ if (isempty(canonical_type))
+ canonical_type = NULL;
+
+ if (!streq_ptr(name, canonical_name) ||
+ !streq_ptr(type, canonical_type) ||
+ !streq_ptr(domain, canonical_domain)) {
+
+ printf("%*s(", (int) indent, "");
+
+ if (canonical_name)
+ printf("%s/", canonical_name);
+ if (canonical_type)
+ printf("%s/", canonical_type);
+
+ printf("%s)\n", canonical_domain);
+ }
+
+ print_source(flags, ts);
+
+ return 0;
+}
+
+static int show_statistics(sd_bus *bus) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ uint64_t n_current_transactions, n_total_transactions,
+ cache_size, n_cache_hit, n_cache_miss,
+ n_dnssec_secure, n_dnssec_insecure, n_dnssec_bogus, n_dnssec_indeterminate;
+ int r, dnssec_supported;
+
+ assert(bus);
+
+ r = sd_bus_get_property_trivial(bus,
+ "org.freedesktop.resolve1",
+ "/org/freedesktop/resolve1",
+ "org.freedesktop.resolve1.Manager",
+ "DNSSECSupported",
+ &error,
+ 'b',
+ &dnssec_supported);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get DNSSEC supported state: %s", bus_error_message(&error, r));
+
+ printf("DNSSEC supported by current servers: %s%s%s\n\n",
+ ansi_highlight(),
+ yes_no(dnssec_supported),
+ ansi_normal());
+
+ r = sd_bus_get_property(bus,
+ "org.freedesktop.resolve1",
+ "/org/freedesktop/resolve1",
+ "org.freedesktop.resolve1.Manager",
+ "TransactionStatistics",
+ &error,
+ &reply,
+ "(tt)");
+ if (r < 0)
+ return log_error_errno(r, "Failed to get transaction statistics: %s", bus_error_message(&error, r));
+
+ r = sd_bus_message_read(reply, "(tt)",
+ &n_current_transactions,
+ &n_total_transactions);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ printf("%sTransactions%s\n"
+ "Current Transactions: %" PRIu64 "\n"
+ " Total Transactions: %" PRIu64 "\n",
+ ansi_highlight(),
+ ansi_normal(),
+ n_current_transactions,
+ n_total_transactions);
+
+ reply = sd_bus_message_unref(reply);
+
+ r = sd_bus_get_property(bus,
+ "org.freedesktop.resolve1",
+ "/org/freedesktop/resolve1",
+ "org.freedesktop.resolve1.Manager",
+ "CacheStatistics",
+ &error,
+ &reply,
+ "(ttt)");
+ if (r < 0)
+ return log_error_errno(r, "Failed to get cache statistics: %s", bus_error_message(&error, r));
+
+ r = sd_bus_message_read(reply, "(ttt)",
+ &cache_size,
+ &n_cache_hit,
+ &n_cache_miss);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ printf("\n%sCache%s\n"
+ " Current Cache Size: %" PRIu64 "\n"
+ " Cache Hits: %" PRIu64 "\n"
+ " Cache Misses: %" PRIu64 "\n",
+ ansi_highlight(),
+ ansi_normal(),
+ cache_size,
+ n_cache_hit,
+ n_cache_miss);
+
+ reply = sd_bus_message_unref(reply);
+
+ r = sd_bus_get_property(bus,
+ "org.freedesktop.resolve1",
+ "/org/freedesktop/resolve1",
+ "org.freedesktop.resolve1.Manager",
+ "DNSSECStatistics",
+ &error,
+ &reply,
+ "(tttt)");
+ if (r < 0)
+ return log_error_errno(r, "Failed to get DNSSEC statistics: %s", bus_error_message(&error, r));
+
+ r = sd_bus_message_read(reply, "(tttt)",
+ &n_dnssec_secure,
+ &n_dnssec_insecure,
+ &n_dnssec_bogus,
+ &n_dnssec_indeterminate);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ printf("\n%sDNSSEC%s\n"
+ " Secure RRsets: %" PRIu64 "\n"
+ " Insecure RRsets: %" PRIu64 "\n"
+ " Bogus RRsets: %" PRIu64 "\n"
+ "Indeterminate RRsets: %" PRIu64 "\n",
+ ansi_highlight(),
+ ansi_normal(),
+ n_dnssec_secure,
+ n_dnssec_insecure,
+ n_dnssec_bogus,
+ n_dnssec_indeterminate);
+
+ return 0;
+}
+
+static int reset_statistics(sd_bus *bus) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ int r;
+
+ r = sd_bus_call_method(bus,
+ "org.freedesktop.resolve1",
+ "/org/freedesktop/resolve1",
+ "org.freedesktop.resolve1.Manager",
+ "ResetStatistics",
+ &error,
+ NULL,
+ NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to reset statistics: %s", bus_error_message(&error, r));
+
+ return 0;
+}
+
static void help_dns_types(void) {
int i;
const char *t;
@@ -464,33 +927,56 @@ static void help_dns_classes(void) {
}
static void help(void) {
- printf("%s [OPTIONS...]\n\n"
- "Resolve IPv4 or IPv6 addresses.\n\n"
- " -h --help Show this help\n"
- " --version Show package version\n"
- " -4 Resolve IPv4 addresses\n"
- " -6 Resolve IPv6 addresses\n"
- " -i INTERFACE Look on interface\n"
- " -p --protocol=PROTOCOL Look via protocol\n"
- " -t --type=TYPE Query RR with DNS type\n"
- " -c --class=CLASS Query RR with DNS class\n"
- " --legend[=BOOL] Do [not] print column headers\n"
- , program_invocation_short_name);
+ printf("%s [OPTIONS...] NAME...\n"
+ "%s [OPTIONS...] --service [[NAME] TYPE] DOMAIN\n\n"
+ "Resolve domain names, IPv4 or IPv6 addresses, resource records, and services.\n\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ " -4 Resolve IPv4 addresses\n"
+ " -6 Resolve IPv6 addresses\n"
+ " -i --interface=INTERFACE Look on interface\n"
+ " -p --protocol=PROTOCOL Look via protocol\n"
+ " -t --type=TYPE Query RR with DNS type\n"
+ " -c --class=CLASS Query RR with DNS class\n"
+ " --service Resolve service (SRV)\n"
+ " --service-address=BOOL Do [not] resolve address for services\n"
+ " --service-txt=BOOL Do [not] resolve TXT records for services\n"
+ " --cname=BOOL Do [not] follow CNAME redirects\n"
+ " --search=BOOL Do [not] use search domains\n"
+ " --legend=BOOL Do [not] print column headers\n"
+ " --statistics Show resolver statistics\n"
+ " --reset-statistics Reset resolver statistics\n"
+ , program_invocation_short_name, program_invocation_short_name);
}
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_LEGEND,
+ ARG_SERVICE,
+ ARG_CNAME,
+ ARG_SERVICE_ADDRESS,
+ ARG_SERVICE_TXT,
+ ARG_SEARCH,
+ ARG_STATISTICS,
+ ARG_RESET_STATISTICS,
};
static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, ARG_VERSION },
- { "type", required_argument, NULL, 't' },
- { "class", required_argument, NULL, 'c' },
- { "legend", optional_argument, NULL, ARG_LEGEND },
- { "protocol", required_argument, NULL, 'p' },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "type", required_argument, NULL, 't' },
+ { "class", required_argument, NULL, 'c' },
+ { "legend", required_argument, NULL, ARG_LEGEND },
+ { "interface", required_argument, NULL, 'i' },
+ { "protocol", required_argument, NULL, 'p' },
+ { "cname", required_argument, NULL, ARG_CNAME },
+ { "service", no_argument, NULL, ARG_SERVICE },
+ { "service-address", required_argument, NULL, ARG_SERVICE_ADDRESS },
+ { "service-txt", required_argument, NULL, ARG_SERVICE_TXT },
+ { "search", required_argument, NULL, ARG_SEARCH },
+ { "statistics", no_argument, NULL, ARG_STATISTICS, },
+ { "reset-statistics", no_argument, NULL, ARG_RESET_STATISTICS },
{}
};
@@ -507,9 +993,7 @@ static int parse_argv(int argc, char *argv[]) {
return 0; /* done */;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0 /* done */;
+ return version();
case '4':
arg_family = AF_INET;
@@ -522,7 +1006,7 @@ static int parse_argv(int argc, char *argv[]) {
case 'i': {
int ifi;
- if (safe_atoi(optarg, &ifi) >= 0 && ifi > 0)
+ if (parse_ifindex(optarg, &ifi) >= 0)
arg_ifindex = ifi;
else {
ifi = if_nametoindex(optarg);
@@ -541,13 +1025,15 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
}
- arg_type = dns_type_from_string(optarg);
- if (arg_type < 0) {
+ r = dns_type_from_string(optarg);
+ if (r < 0) {
log_error("Failed to parse RR record type %s", optarg);
- return arg_type;
+ return r;
}
- assert(arg_type > 0 && (uint16_t) arg_type == arg_type);
+ arg_type = (uint16_t) r;
+ assert((int) arg_type == r);
+ arg_mode = MODE_RESOLVE_RECORD;
break;
case 'c':
@@ -556,25 +1042,22 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
}
- r = dns_class_from_string(optarg, &arg_class);
+ r = dns_class_from_string(optarg);
if (r < 0) {
log_error("Failed to parse RR record class %s", optarg);
return r;
}
+ arg_class = (uint16_t) r;
+ assert((int) arg_class == r);
break;
case ARG_LEGEND:
- if (optarg) {
- r = parse_boolean(optarg);
- if (r < 0) {
- log_error("Failed to parse --legend= argument");
- return r;
- }
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --legend= argument");
- arg_legend = !!r;
- } else
- arg_legend = false;
+ arg_legend = r;
break;
case 'p':
@@ -593,6 +1076,58 @@ static int parse_argv(int argc, char *argv[]) {
break;
+ case ARG_SERVICE:
+ arg_mode = MODE_RESOLVE_SERVICE;
+ break;
+
+ case ARG_CNAME:
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --cname= argument.");
+ if (r == 0)
+ arg_flags |= SD_RESOLVED_NO_CNAME;
+ else
+ arg_flags &= ~SD_RESOLVED_NO_CNAME;
+ break;
+
+ case ARG_SERVICE_ADDRESS:
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --service-address= argument.");
+ if (r == 0)
+ arg_flags |= SD_RESOLVED_NO_ADDRESS;
+ else
+ arg_flags &= ~SD_RESOLVED_NO_ADDRESS;
+ break;
+
+ case ARG_SERVICE_TXT:
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --service-txt= argument.");
+ if (r == 0)
+ arg_flags |= SD_RESOLVED_NO_TXT;
+ else
+ arg_flags &= ~SD_RESOLVED_NO_TXT;
+ break;
+
+ case ARG_SEARCH:
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --search argument.");
+ if (r == 0)
+ arg_flags |= SD_RESOLVED_NO_SEARCH;
+ else
+ arg_flags &= ~SD_RESOLVED_NO_SEARCH;
+ break;
+
+ case ARG_STATISTICS:
+ arg_mode = MODE_STATISTICS;
+ break;
+
+ case ARG_RESET_STATISTICS:
+ arg_mode = MODE_RESET_STATISTICS;
+ break;
+
case '?':
return -EINVAL;
@@ -601,18 +1136,26 @@ static int parse_argv(int argc, char *argv[]) {
}
if (arg_type == 0 && arg_class != 0) {
- log_error("--class= may only be used in conjunction with --type=");
+ log_error("--class= may only be used in conjunction with --type=.");
+ return -EINVAL;
+ }
+
+ if (arg_type != 0 && arg_mode != MODE_RESOLVE_RECORD) {
+ log_error("--service and --type= may not be combined.");
return -EINVAL;
}
if (arg_type != 0 && arg_class == 0)
arg_class = DNS_CLASS_IN;
+ if (arg_class != 0 && arg_type == 0)
+ arg_type = DNS_TYPE_A;
+
return 1 /* work to do */;
}
int main(int argc, char **argv) {
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
log_parse_environment();
@@ -622,36 +1165,99 @@ int main(int argc, char **argv) {
if (r <= 0)
goto finish;
- if (optind >= argc) {
- log_error("No arguments passed");
- r = -EINVAL;
- goto finish;
- }
-
r = sd_bus_open_system(&bus);
if (r < 0) {
log_error_errno(r, "sd_bus_open_system: %m");
goto finish;
}
- while (argv[optind]) {
- int family, ifindex, k;
- union in_addr_union a;
+ switch (arg_mode) {
- if (arg_type != 0)
- k = resolve_record(bus, argv[optind]);
+ case MODE_RESOLVE_HOST:
+ if (optind >= argc) {
+ log_error("No arguments passed");
+ r = -EINVAL;
+ goto finish;
+ }
+
+ while (argv[optind]) {
+ int family, ifindex, k;
+ union in_addr_union a;
+
+ if (startswith(argv[optind], "dns:"))
+ k = resolve_rfc4501(bus, argv[optind]);
+ else {
+ k = parse_address(argv[optind], &family, &a, &ifindex);
+ if (k >= 0)
+ k = resolve_address(bus, family, &a, ifindex);
+ else
+ k = resolve_host(bus, argv[optind]);
+ }
+
+ if (r == 0)
+ r = k;
+
+ optind++;
+ }
+ break;
+
+ case MODE_RESOLVE_RECORD:
+ if (optind >= argc) {
+ log_error("No arguments passed");
+ r = -EINVAL;
+ goto finish;
+ }
+
+ while (argv[optind]) {
+ int k;
+
+ k = resolve_record(bus, argv[optind], arg_class, arg_type);
+ if (r == 0)
+ r = k;
+
+ optind++;
+ }
+ break;
+
+ case MODE_RESOLVE_SERVICE:
+ if (argc < optind + 1) {
+ log_error("Domain specification required.");
+ r = -EINVAL;
+ goto finish;
+
+ } else if (argc == optind + 1)
+ r = resolve_service(bus, NULL, NULL, argv[optind]);
+ else if (argc == optind + 2)
+ r = resolve_service(bus, NULL, argv[optind], argv[optind+1]);
+ else if (argc == optind + 3)
+ r = resolve_service(bus, argv[optind], argv[optind+1], argv[optind+2]);
else {
- k = parse_address(argv[optind], &family, &a, &ifindex);
- if (k >= 0)
- k = resolve_address(bus, family, &a, ifindex);
- else
- k = resolve_host(bus, argv[optind]);
+ log_error("Too many arguments");
+ r = -EINVAL;
+ goto finish;
+ }
+
+ break;
+
+ case MODE_STATISTICS:
+ if (argc > optind) {
+ log_error("Too many arguments.");
+ r = -EINVAL;
+ goto finish;
}
- if (r == 0)
- r = k;
+ r = show_statistics(bus);
+ break;
+
+ case MODE_RESET_STATISTICS:
+ if (argc > optind) {
+ log_error("Too many arguments.");
+ r = -EINVAL;
+ goto finish;
+ }
- optind++;
+ r = reset_statistics(bus);
+ break;
}
finish:
diff --git a/src/resolve/RFCs b/src/resolve/RFCs
new file mode 100644
index 0000000000..22004a00cd
--- /dev/null
+++ b/src/resolve/RFCs
@@ -0,0 +1,59 @@
+Y = Comprehensively Implemented, to the point appropriate for resolved
+D = Comprehensively Implemented, by a dependency of resolved
+! = Missing and something we might want to implement
+~ = Needs no explicit support or doesn't apply
+? = Is this relevant today?
+ = We are working on this
+
+Y https://tools.ietf.org/html/rfc1034 → DOMAIN NAMES - CONCEPTS AND FACILITIES
+Y https://tools.ietf.org/html/rfc1035 → DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION
+? https://tools.ietf.org/html/rfc1101 → DNS Encoding of Network Names and Other Types
+Y https://tools.ietf.org/html/rfc1123 → Requirements for Internet Hosts -- Application and Support
+~ https://tools.ietf.org/html/rfc1464 → Using the Domain Name System To Store Arbitrary String Attributes
+Y https://tools.ietf.org/html/rfc1536 → Common DNS Implementation Errors and Suggested Fixes
+Y https://tools.ietf.org/html/rfc1876 → A Means for Expressing Location Information in the Domain Name System
+Y https://tools.ietf.org/html/rfc2181 → Clarifications to the DNS Specification
+Y https://tools.ietf.org/html/rfc2308 → Negative Caching of DNS Queries (DNS NCACHE)
+Y https://tools.ietf.org/html/rfc2782 → A DNS RR for specifying the location of services (DNS SRV)
+D https://tools.ietf.org/html/rfc3492 → Punycode: A Bootstring encoding of Unicode for Internationalized Domain Names in Applications (IDNA)
+Y https://tools.ietf.org/html/rfc3596 → DNS Extensions to Support IP Version 6
+Y https://tools.ietf.org/html/rfc3597 → Handling of Unknown DNS Resource Record (RR) Types
+Y https://tools.ietf.org/html/rfc4033 → DNS Security Introduction and Requirements
+Y https://tools.ietf.org/html/rfc4034 → Resource Records for the DNS Security Extensions
+Y https://tools.ietf.org/html/rfc4035 → Protocol Modifications for the DNS Security Extensions
+! https://tools.ietf.org/html/rfc4183 → A Suggested Scheme for DNS Resolution of Networks and Gateways
+Y https://tools.ietf.org/html/rfc4255 → Using DNS to Securely Publish Secure Shell (SSH) Key Fingerprints
+Y https://tools.ietf.org/html/rfc4343 → Domain Name System (DNS) Case Insensitivity Clarification
+~ https://tools.ietf.org/html/rfc4470 → Minimally Covering NSEC Records and DNSSEC On-line Signing
+Y https://tools.ietf.org/html/rfc4501 → Domain Name System Uniform Resource Identifiers
+Y https://tools.ietf.org/html/rfc4509 → Use of SHA-256 in DNSSEC Delegation Signer (DS) Resource Records (RRs)
+~ https://tools.ietf.org/html/rfc4592 → The Role of Wildcards in the Domain Name System
+~ https://tools.ietf.org/html/rfc4697 → Observed DNS Resolution Misbehavior
+Y https://tools.ietf.org/html/rfc4795 → Link-Local Multicast Name Resolution (LLMNR)
+Y https://tools.ietf.org/html/rfc5011 → Automated Updates of DNS Security (DNSSEC) Trust Anchors
+Y https://tools.ietf.org/html/rfc5155 → DNS Security (DNSSEC) Hashed Authenticated Denial of Existence
+Y https://tools.ietf.org/html/rfc5452 → Measures for Making DNS More Resilient against Forged Answers
+Y https://tools.ietf.org/html/rfc5702 → Use of SHA-2 Algorithms with RSA in DNSKEY and RRSIG Resource Records for DNSSEC
+Y https://tools.ietf.org/html/rfc5890 → Internationalized Domain Names for Applications (IDNA): Definitions and Document Framework
+Y https://tools.ietf.org/html/rfc5891 → Internationalized Domain Names in Applications (IDNA): Protocol
+Y https://tools.ietf.org/html/rfc5966 → DNS Transport over TCP - Implementation Requirements
+Y https://tools.ietf.org/html/rfc6303 → Locally Served DNS Zones
+Y https://tools.ietf.org/html/rfc6604 → xNAME RCODE and Status Bits Clarification
+Y https://tools.ietf.org/html/rfc6605 → Elliptic Curve Digital Signature Algorithm (DSA) for DNSSEC
+ https://tools.ietf.org/html/rfc6672 → DNAME Redirection in the DNS
+! https://tools.ietf.org/html/rfc6731 → Improved Recursive DNS Server Selection for Multi-Interfaced Nodes
+Y https://tools.ietf.org/html/rfc6761 → Special-Use Domain Names
+ https://tools.ietf.org/html/rfc6762 → Multicast DNS
+ https://tools.ietf.org/html/rfc6763 → DNS-Based Service Discovery
+~ https://tools.ietf.org/html/rfc6781 → DNSSEC Operational Practices, Version 2
+Y https://tools.ietf.org/html/rfc6840 → Clarifications and Implementation Notes for DNS Security (DNSSEC)
+Y https://tools.ietf.org/html/rfc6891 → Extension Mechanisms for DNS (EDNS(0))
+Y https://tools.ietf.org/html/rfc6944 → Applicability Statement: DNS Security (DNSSEC) DNSKEY Algorithm Implementation Status
+Y https://tools.ietf.org/html/rfc6975 → Signaling Cryptographic Algorithm Understanding in DNS Security Extensions (DNSSEC)
+Y https://tools.ietf.org/html/rfc7129 → Authenticated Denial of Existence in the DNS
+Y https://tools.ietf.org/html/rfc7646 → Definition and Use of DNSSEC Negative Trust Anchors
+~ https://tools.ietf.org/html/rfc7719 → DNS Terminology
+
+Also relevant:
+
+ https://www.iab.org/documents/correspondence-reports-documents/2013-2/iab-statement-dotless-domains-considered-harmful/
diff --git a/src/resolve/dns-type.c b/src/resolve/dns-type.c
index 63b4b36e88..058d14009a 100644
--- a/src/resolve/dns-type.c
+++ b/src/resolve/dns-type.c
@@ -20,6 +20,7 @@
***/
#include "dns-type.h"
+#include "string-util.h"
typedef const struct {
uint16_t type;
@@ -44,7 +45,167 @@ int dns_type_from_string(const char *s) {
return sc->id;
}
-/* XXX: find an authoritative list of all pseudo types? */
-bool dns_type_is_pseudo(int n) {
- return IN_SET(n, DNS_TYPE_ANY, DNS_TYPE_AXFR, DNS_TYPE_IXFR, DNS_TYPE_OPT);
+bool dns_type_is_pseudo(uint16_t type) {
+
+ /* Checks whether the specified type is a "pseudo-type". What
+ * a "pseudo-type" precisely is, is defined only very weakly,
+ * but apparently entails all RR types that are not actually
+ * stored as RRs on the server and should hence also not be
+ * cached. We use this list primarily to validate NSEC type
+ * bitfields, and to verify what to cache. */
+
+ return IN_SET(type,
+ 0, /* A Pseudo RR type, according to RFC 2931 */
+ DNS_TYPE_ANY,
+ DNS_TYPE_AXFR,
+ DNS_TYPE_IXFR,
+ DNS_TYPE_OPT,
+ DNS_TYPE_TSIG,
+ DNS_TYPE_TKEY
+ );
+}
+
+bool dns_class_is_pseudo(uint16_t class) {
+ return class == DNS_TYPE_ANY;
+}
+
+bool dns_type_is_valid_query(uint16_t type) {
+
+ /* The types valid as questions in packets */
+
+ return !IN_SET(type,
+ 0,
+ DNS_TYPE_OPT,
+ DNS_TYPE_TSIG,
+ DNS_TYPE_TKEY,
+
+ /* RRSIG are technically valid as questions, but we refuse doing explicit queries for them, as
+ * they aren't really payload, but signatures for payload, and cannot be validated on their
+ * own. After all they are the signatures, and have no signatures of their own validating
+ * them. */
+ DNS_TYPE_RRSIG);
+}
+
+bool dns_type_is_valid_rr(uint16_t type) {
+
+ /* The types valid as RR in packets (but not necessarily
+ * stored on servers). */
+
+ return !IN_SET(type,
+ DNS_TYPE_ANY,
+ DNS_TYPE_AXFR,
+ DNS_TYPE_IXFR);
+}
+
+bool dns_class_is_valid_rr(uint16_t class) {
+ return class != DNS_CLASS_ANY;
+}
+
+bool dns_type_may_redirect(uint16_t type) {
+ /* The following record types should never be redirected using
+ * CNAME/DNAME RRs. See
+ * <https://tools.ietf.org/html/rfc4035#section-2.5>. */
+
+ if (dns_type_is_pseudo(type))
+ return false;
+
+ return !IN_SET(type,
+ DNS_TYPE_CNAME,
+ DNS_TYPE_DNAME,
+ DNS_TYPE_NSEC3,
+ DNS_TYPE_NSEC,
+ DNS_TYPE_RRSIG,
+ DNS_TYPE_NXT,
+ DNS_TYPE_SIG,
+ DNS_TYPE_KEY);
+}
+
+bool dns_type_may_wildcard(uint16_t type) {
+
+ /* The following records may not be expanded from wildcard RRsets */
+
+ if (dns_type_is_pseudo(type))
+ return false;
+
+ return !IN_SET(type,
+ DNS_TYPE_NSEC3,
+ DNS_TYPE_SOA,
+
+ /* Prohibited by https://tools.ietf.org/html/rfc4592#section-4.4 */
+ DNS_TYPE_DNAME);
+}
+
+bool dns_type_apex_only(uint16_t type) {
+
+ /* Returns true for all RR types that may only appear signed in a zone apex */
+
+ return IN_SET(type,
+ DNS_TYPE_SOA,
+ DNS_TYPE_NS, /* this one can appear elsewhere, too, but not signed */
+ DNS_TYPE_DNSKEY,
+ DNS_TYPE_NSEC3PARAM);
+}
+
+bool dns_type_is_dnssec(uint16_t type) {
+ return IN_SET(type,
+ DNS_TYPE_DS,
+ DNS_TYPE_DNSKEY,
+ DNS_TYPE_RRSIG,
+ DNS_TYPE_NSEC,
+ DNS_TYPE_NSEC3,
+ DNS_TYPE_NSEC3PARAM);
+}
+
+bool dns_type_is_obsolete(uint16_t type) {
+ return IN_SET(type,
+ /* Obsoleted by RFC 973 */
+ DNS_TYPE_MD,
+ DNS_TYPE_MF,
+ DNS_TYPE_MAILA,
+
+ /* Kinda obsoleted by RFC 2505 */
+ DNS_TYPE_MB,
+ DNS_TYPE_MG,
+ DNS_TYPE_MR,
+ DNS_TYPE_MINFO,
+ DNS_TYPE_MAILB,
+
+ /* RFC1127 kinda obsoleted this by recommending against its use */
+ DNS_TYPE_WKS,
+
+ /* Declared historical by RFC 6563 */
+ DNS_TYPE_A6,
+
+ /* Obsoleted by DNSSEC-bis */
+ DNS_TYPE_NXT,
+
+ /* RFC 1035 removed support for concepts that needed this from RFC 883 */
+ DNS_TYPE_NULL);
+}
+
+const char *dns_class_to_string(uint16_t class) {
+
+ switch (class) {
+
+ case DNS_CLASS_IN:
+ return "IN";
+
+ case DNS_CLASS_ANY:
+ return "ANY";
+ }
+
+ return NULL;
+}
+
+int dns_class_from_string(const char *s) {
+
+ if (!s)
+ return _DNS_CLASS_INVALID;
+
+ if (strcaseeq(s, "IN"))
+ return DNS_CLASS_IN;
+ else if (strcaseeq(s, "ANY"))
+ return DNS_CLASS_ANY;
+
+ return _DNS_CLASS_INVALID;
}
diff --git a/src/resolve/dns-type.h b/src/resolve/dns-type.h
index 950af36ee3..78ff71b06e 100644
--- a/src/resolve/dns-type.h
+++ b/src/resolve/dns-type.h
@@ -23,10 +23,6 @@
#include "macro.h"
-const char *dns_type_to_string(int type);
-int dns_type_from_string(const char *s);
-bool dns_type_is_pseudo(int n);
-
/* DNS record types, taken from
* http://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml.
*/
@@ -119,3 +115,30 @@ enum {
assert_cc(DNS_TYPE_SSHFP == 44);
assert_cc(DNS_TYPE_TLSA == 52);
assert_cc(DNS_TYPE_ANY == 255);
+
+/* DNS record classes, see RFC 1035 */
+enum {
+ DNS_CLASS_IN = 0x01,
+ DNS_CLASS_ANY = 0xFF,
+
+ _DNS_CLASS_MAX,
+ _DNS_CLASS_INVALID = -1
+};
+
+bool dns_type_is_pseudo(uint16_t type);
+bool dns_type_is_valid_query(uint16_t type);
+bool dns_type_is_valid_rr(uint16_t type);
+bool dns_type_may_redirect(uint16_t type);
+bool dns_type_is_dnssec(uint16_t type);
+bool dns_type_is_obsolete(uint16_t type);
+bool dns_type_may_wildcard(uint16_t type);
+bool dns_type_apex_only(uint16_t type);
+
+bool dns_class_is_pseudo(uint16_t class);
+bool dns_class_is_valid_rr(uint16_t class);
+
+const char *dns_type_to_string(int type);
+int dns_type_from_string(const char *s);
+
+const char *dns_class_to_string(uint16_t type);
+int dns_class_from_string(const char *name);
diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c
index bf1b7c8ab4..9110ea52a6 100644
--- a/src/resolve/resolved-bus.c
+++ b/src/resolve/resolved-bus.c
@@ -19,27 +19,15 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "alloc-util.h"
#include "bus-common-errors.h"
#include "bus-util.h"
-
#include "dns-domain.h"
#include "resolved-bus.h"
#include "resolved-def.h"
+#include "resolved-link-bus.h"
static int reply_query_state(DnsQuery *q) {
- _cleanup_free_ char *ip = NULL;
- const char *name;
- int r;
-
- if (q->request_hostname)
- name = q->request_hostname;
- else {
- r = in_addr_to_string(q->request_family, &q->request_address, &ip);
- if (r < 0)
- return r;
-
- name = ip;
- }
switch (q->state) {
@@ -61,11 +49,21 @@ static int reply_query_state(DnsQuery *q) {
case DNS_TRANSACTION_ABORTED:
return sd_bus_reply_method_errorf(q->request, BUS_ERROR_ABORTED, "Query aborted");
- case DNS_TRANSACTION_FAILURE: {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ case DNS_TRANSACTION_DNSSEC_FAILED:
+ return sd_bus_reply_method_errorf(q->request, BUS_ERROR_DNSSEC_FAILED, "DNSSEC validation failed: %s",
+ dnssec_result_to_string(q->answer_dnssec_result));
+
+ case DNS_TRANSACTION_NO_TRUST_ANCHOR:
+ return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_TRUST_ANCHOR, "No suitable trust anchor known");
+
+ case DNS_TRANSACTION_RR_TYPE_UNSUPPORTED:
+ return sd_bus_reply_method_errorf(q->request, BUS_ERROR_RR_TYPE_UNSUPPORTED, "Server does not support requested resource record type");
+
+ case DNS_TRANSACTION_RCODE_FAILURE: {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
if (q->answer_rcode == DNS_RCODE_NXDOMAIN)
- sd_bus_error_setf(&error, _BUS_ERROR_DNS "NXDOMAIN", "'%s' not found", name);
+ sd_bus_error_setf(&error, _BUS_ERROR_DNS "NXDOMAIN", "'%s' not found", dns_query_string(q));
else {
const char *rc, *n;
char p[3]; /* the rcode is 4 bits long */
@@ -77,7 +75,7 @@ static int reply_query_state(DnsQuery *q) {
}
n = strjoina(_BUS_ERROR_DNS, rc);
- sd_bus_error_setf(&error, n, "Could not resolve '%s', server or network returned error %s", name, rc);
+ sd_bus_error_setf(&error, n, "Could not resolve '%s', server or network returned error %s", dns_query_string(q), rc);
}
return sd_bus_reply_method_error(q->request, &error);
@@ -85,6 +83,7 @@ static int reply_query_state(DnsQuery *q) {
case DNS_TRANSACTION_NULL:
case DNS_TRANSACTION_PENDING:
+ case DNS_TRANSACTION_VALIDATING:
case DNS_TRANSACTION_SUCCESS:
default:
assert_not_reached("Impossible state");
@@ -132,10 +131,9 @@ static int append_address(sd_bus_message *reply, DnsResourceRecord *rr, int ifin
}
static void bus_method_resolve_hostname_complete(DnsQuery *q) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *cname = NULL, *canonical = NULL;
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
- unsigned added = 0, i;
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ unsigned added = 0;
int r;
assert(q);
@@ -145,6 +143,16 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
goto finish;
}
+ r = dns_query_process_cname(q);
+ if (r == -ELOOP) {
+ r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q));
+ goto finish;
+ }
+ if (r < 0)
+ goto finish;
+ if (r == DNS_QUERY_RESTARTED) /* This was a cname, and the query was restarted. */
+ return;
+
r = sd_bus_message_new_method_return(q->request, &reply);
if (r < 0)
goto finish;
@@ -154,92 +162,46 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
goto finish;
if (q->answer) {
- answer = dns_answer_ref(q->answer);
-
- for (i = 0; i < answer->n_rrs; i++) {
- r = dns_question_matches_rr(q->question, answer->items[i].rr);
- if (r < 0)
- goto finish;
- if (r == 0) {
- /* Hmm, if this is not an address record,
- maybe it's a cname? If so, remember this */
- r = dns_question_matches_cname(q->question, answer->items[i].rr);
- if (r < 0)
- goto finish;
- if (r > 0)
- cname = dns_resource_record_ref(answer->items[i].rr);
-
- continue;
- }
-
- r = append_address(reply, answer->items[i].rr, answer->items[i].ifindex);
- if (r < 0)
- goto finish;
-
- if (!canonical)
- canonical = dns_resource_record_ref(answer->items[i].rr);
-
- added ++;
- }
- }
-
- 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;
- }
+ DnsResourceRecord *rr;
+ int ifindex;
- /* This has a cname? Then update the query with the
- * new cname. */
- r = dns_query_cname_redirect(q, cname);
- if (r < 0) {
- if (r == -ELOOP)
- r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop on '%s'", q->request_hostname);
- else
- r = sd_bus_reply_method_errno(q->request, -r, NULL);
+ DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
+ DnsQuestion *question;
- goto finish;
- }
+ question = dns_query_question_for_protocol(q, q->answer_protocol);
- /* Before we restart the query, let's see if any of
- * the RRs we already got already answers our query */
- for (i = 0; i < answer->n_rrs; i++) {
- r = dns_question_matches_rr(q->question, answer->items[i].rr);
+ r = dns_question_matches_rr(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
if (r < 0)
goto finish;
if (r == 0)
continue;
- r = append_address(reply, answer->items[i].rr, answer->items[i].ifindex);
+ r = append_address(reply, rr, ifindex);
if (r < 0)
goto finish;
if (!canonical)
- canonical = dns_resource_record_ref(answer->items[i].rr);
+ canonical = dns_resource_record_ref(rr);
- added++;
+ added ++;
}
+ }
- /* If we didn't find anything, then let's restart the
- * query, this time with the cname */
- if (added <= 0) {
- r = dns_query_go(q);
- if (r < 0) {
- r = sd_bus_reply_method_errno(q->request, -r, NULL);
- goto finish;
- }
-
- return;
- }
+ if (added <= 0) {
+ r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_query_string(q));
+ goto finish;
}
r = sd_bus_message_close_container(reply);
if (r < 0)
goto finish;
- /* Return the precise spelling and uppercasing reported by the server */
+ /* Return the precise spelling and uppercasing and CNAME target reported by the server */
assert(canonical);
- r = sd_bus_message_append(reply, "st", DNS_RESOURCE_KEY_NAME(canonical->key), SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family));
+ r = sd_bus_message_append(
+ reply, "st",
+ DNS_RESOURCE_KEY_NAME(canonical->key),
+ SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, q->answer_authenticated));
if (r < 0)
goto finish;
@@ -248,29 +210,29 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
finish:
if (r < 0) {
log_error_errno(r, "Failed to send hostname reply: %m");
- sd_bus_reply_method_errno(q->request, -r, NULL);
+ sd_bus_reply_method_errno(q->request, r, NULL);
}
dns_query_free(q);
}
-static int check_ifindex_flags(int ifindex, uint64_t *flags, sd_bus_error *error) {
+static int check_ifindex_flags(int ifindex, uint64_t *flags, uint64_t ok, sd_bus_error *error) {
assert(flags);
if (ifindex < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
- if (*flags & ~SD_RESOLVED_FLAGS_ALL)
+ if (*flags & ~(SD_RESOLVED_PROTOCOLS_ALL|SD_RESOLVED_NO_CNAME|ok))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid flags parameter");
- if (*flags == 0)
- *flags = SD_RESOLVED_FLAGS_DEFAULT;
+ if ((*flags & SD_RESOLVED_PROTOCOLS_ALL) == 0) /* If no protocol is enabled, enable all */
+ *flags |= SD_RESOLVED_PROTOCOLS_ALL;
return 0;
}
static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
+ _cleanup_(dns_question_unrefp) DnsQuestion *question_idna = NULL, *question_utf8 = NULL;
Manager *m = userdata;
const char *hostname;
int family, ifindex;
@@ -281,6 +243,8 @@ static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata,
assert(message);
assert(m);
+ assert_cc(sizeof(int) == sizeof(int32_t));
+
r = sd_bus_message_read(message, "isit", &ifindex, &hostname, &family, &flags);
if (r < 0)
return r;
@@ -288,69 +252,53 @@ static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata,
if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
- r = dns_name_normalize(hostname, NULL);
+ r = dns_name_is_valid(hostname);
if (r < 0)
+ return r;
+ if (r == 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", hostname);
- r = check_ifindex_flags(ifindex, &flags, error);
+ r = check_ifindex_flags(ifindex, &flags, SD_RESOLVED_NO_SEARCH, error);
if (r < 0)
return r;
- question = dns_question_new(family == AF_UNSPEC ? 2 : 1);
- if (!question)
- return -ENOMEM;
-
- if (family != AF_INET6) {
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
-
- key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, hostname);
- if (!key)
- return -ENOMEM;
-
- r = dns_question_add(question, key);
- if (r < 0)
- return r;
- }
-
- if (family != AF_INET) {
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
-
- key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, hostname);
- if (!key)
- return -ENOMEM;
+ r = dns_question_new_address(&question_utf8, family, hostname, false);
+ if (r < 0)
+ return r;
- r = dns_question_add(question, key);
- if (r < 0)
- return r;
- }
+ r = dns_question_new_address(&question_idna, family, hostname, true);
+ if (r < 0)
+ return r;
- r = dns_query_new(m, &q, question, ifindex, flags);
+ r = dns_query_new(m, &q, question_utf8, question_idna, ifindex, flags);
if (r < 0)
return r;
q->request = sd_bus_message_ref(message);
q->request_family = family;
- q->request_hostname = hostname;
q->complete = bus_method_resolve_hostname_complete;
r = dns_query_bus_track(q, message);
if (r < 0)
- return r;
+ goto fail;
r = dns_query_go(q);
- if (r < 0) {
- dns_query_free(q);
- return r;
- }
+ if (r < 0)
+ goto fail;
return 1;
+
+fail:
+ dns_query_free(q);
+ return r;
}
static void bus_method_resolve_address_complete(DnsQuery *q) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
- unsigned added = 0, i;
- int r;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ DnsQuestion *question;
+ DnsResourceRecord *rr;
+ unsigned added = 0;
+ int ifindex, r;
assert(q);
@@ -359,6 +307,16 @@ static void bus_method_resolve_address_complete(DnsQuery *q) {
goto finish;
}
+ r = dns_query_process_cname(q);
+ if (r == -ELOOP) {
+ r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q));
+ goto finish;
+ }
+ if (r < 0)
+ goto finish;
+ if (r == DNS_QUERY_RESTARTED) /* This was a cname, and the query was restarted. */
+ return;
+
r = sd_bus_message_new_method_return(q->request, &reply);
if (r < 0)
goto finish;
@@ -367,30 +325,27 @@ static void bus_method_resolve_address_complete(DnsQuery *q) {
if (r < 0)
goto finish;
- if (q->answer) {
- answer = dns_answer_ref(q->answer);
+ question = dns_query_question_for_protocol(q, q->answer_protocol);
- for (i = 0; i < answer->n_rrs; i++) {
- r = dns_question_matches_rr(q->question, answer->items[i].rr);
- if (r < 0)
- goto finish;
- if (r == 0)
- continue;
+ DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
+ r = dns_question_matches_rr(question, rr, NULL);
+ if (r < 0)
+ goto finish;
+ if (r == 0)
+ continue;
- r = sd_bus_message_append(reply, "(is)", answer->items[i].ifindex, answer->items[i].rr->ptr.name);
- if (r < 0)
- goto finish;
+ r = sd_bus_message_append(reply, "(is)", ifindex, rr->ptr.name);
+ if (r < 0)
+ goto finish;
- added ++;
- }
+ added ++;
}
- if (added == 0) {
+ if (added <= 0) {
_cleanup_free_ char *ip = NULL;
in_addr_to_string(q->request_family, &q->request_address, &ip);
-
- r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Address '%s' does not have any RR of requested type", ip);
+ r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Address '%s' does not have any RR of requested type", strna(ip));
goto finish;
}
@@ -398,7 +353,7 @@ static void bus_method_resolve_address_complete(DnsQuery *q) {
if (r < 0)
goto finish;
- r = sd_bus_message_append(reply, "t", SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family));
+ r = sd_bus_message_append(reply, "t", SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, q->answer_authenticated));
if (r < 0)
goto finish;
@@ -407,16 +362,14 @@ static void bus_method_resolve_address_complete(DnsQuery *q) {
finish:
if (r < 0) {
log_error_errno(r, "Failed to send address reply: %m");
- sd_bus_reply_method_errno(q->request, -r, NULL);
+ sd_bus_reply_method_errno(q->request, r, NULL);
}
dns_query_free(q);
}
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;
Manager *m = userdata;
int family, ifindex;
uint64_t flags;
@@ -428,6 +381,8 @@ static int bus_method_resolve_address(sd_bus_message *message, void *userdata, s
assert(message);
assert(m);
+ assert_cc(sizeof(int) == sizeof(int32_t));
+
r = sd_bus_message_read(message, "ii", &ifindex, &family);
if (r < 0)
return r;
@@ -446,29 +401,15 @@ static int bus_method_resolve_address(sd_bus_message *message, void *userdata, s
if (r < 0)
return r;
- r = check_ifindex_flags(ifindex, &flags, error);
+ r = check_ifindex_flags(ifindex, &flags, 0, error);
if (r < 0)
return r;
- r = dns_name_reverse(family, d, &reverse);
- if (r < 0)
- return r;
-
- question = dns_question_new(1);
- if (!question)
- return -ENOMEM;
-
- key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, reverse);
- if (!key)
- return -ENOMEM;
-
- reverse = NULL;
-
- r = dns_question_add(question, key);
+ r = dns_question_new_reverse(&question, family, d);
if (r < 0)
return r;
- r = dns_query_new(m, &q, question, ifindex, flags);
+ r = dns_query_new(m, &q, question, question, ifindex, flags|SD_RESOLVED_NO_SEARCH);
if (r < 0)
return r;
@@ -479,21 +420,53 @@ static int bus_method_resolve_address(sd_bus_message *message, void *userdata, s
r = dns_query_bus_track(q, message);
if (r < 0)
- return r;
+ goto fail;
r = dns_query_go(q);
- if (r < 0) {
- dns_query_free(q);
- return r;
- }
+ if (r < 0)
+ goto fail;
return 1;
+
+fail:
+ dns_query_free(q);
+ return r;
+}
+
+static int bus_message_append_rr(sd_bus_message *m, DnsResourceRecord *rr, int ifindex) {
+ int r;
+
+ assert(m);
+ assert(rr);
+
+ r = sd_bus_message_open_container(m, 'r', "iqqay");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(m, "iqq",
+ ifindex,
+ rr->key->class,
+ rr->key->type);
+ if (r < 0)
+ return r;
+
+ r = dns_resource_record_to_wire_format(rr, false);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append_array(m, 'y', rr->wire_format, rr->wire_format_size);
+ if (r < 0)
+ return r;
+
+ return sd_bus_message_close_container(m);
}
static void bus_method_resolve_record_complete(DnsQuery *q) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
- unsigned added = 0, i;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ DnsResourceRecord *rr;
+ DnsQuestion *question;
+ unsigned added = 0;
+ int ifindex;
int r;
assert(q);
@@ -503,6 +476,16 @@ static void bus_method_resolve_record_complete(DnsQuery *q) {
goto finish;
}
+ r = dns_query_process_cname(q);
+ if (r == -ELOOP) {
+ r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q));
+ goto finish;
+ }
+ if (r < 0)
+ goto finish;
+ if (r == DNS_QUERY_RESTARTED) /* This was a cname, and the query was restarted. */
+ return;
+
r = sd_bus_message_new_method_return(q->request, &reply);
if (r < 0)
goto finish;
@@ -511,54 +494,24 @@ static void bus_method_resolve_record_complete(DnsQuery *q) {
if (r < 0)
goto finish;
- if (q->answer) {
- answer = dns_answer_ref(q->answer);
-
- for (i = 0; i < answer->n_rrs; i++) {
- _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
- size_t start;
-
- r = dns_question_matches_rr(q->question, answer->items[i].rr);
- if (r < 0)
- goto finish;
- if (r == 0)
- continue;
-
- r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0);
- if (r < 0)
- goto finish;
-
- p->refuse_compression = true;
+ question = dns_query_question_for_protocol(q, q->answer_protocol);
- r = dns_packet_append_rr(p, answer->items[i].rr, &start);
- if (r < 0)
- goto finish;
-
- r = sd_bus_message_open_container(reply, 'r', "iqqay");
- if (r < 0)
- goto finish;
-
- r = sd_bus_message_append(reply, "iqq",
- answer->items[i].ifindex,
- answer->items[i].rr->key->class,
- answer->items[i].rr->key->type);
- if (r < 0)
- goto finish;
-
- r = sd_bus_message_append_array(reply, 'y', DNS_PACKET_DATA(p) + start, p->size - start);
- if (r < 0)
- goto finish;
+ DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
+ r = dns_question_matches_rr(question, rr, NULL);
+ if (r < 0)
+ goto finish;
+ if (r == 0)
+ continue;
- r = sd_bus_message_close_container(reply);
- if (r < 0)
- goto finish;
+ r = bus_message_append_rr(reply, rr, ifindex);
+ if (r < 0)
+ goto finish;
- added ++;
- }
+ added ++;
}
if (added <= 0) {
- r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Name '%s' does not have any RR of the requested type", q->request_hostname);
+ r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Name '%s' does not have any RR of the requested type", dns_query_string(q));
goto finish;
}
@@ -566,7 +519,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) {
if (r < 0)
goto finish;
- r = sd_bus_message_append(reply, "t", SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family));
+ r = sd_bus_message_append(reply, "t", SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, q->answer_authenticated));
if (r < 0)
goto finish;
@@ -575,7 +528,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) {
finish:
if (r < 0) {
log_error_errno(r, "Failed to send record reply: %m");
- sd_bus_reply_method_errno(q->request, -r, NULL);
+ sd_bus_reply_method_errno(q->request, r, NULL);
}
dns_query_free(q);
@@ -594,15 +547,24 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd
assert(message);
assert(m);
+ assert_cc(sizeof(int) == sizeof(int32_t));
+
r = sd_bus_message_read(message, "isqqt", &ifindex, &name, &class, &type, &flags);
if (r < 0)
return r;
- r = dns_name_normalize(name, NULL);
+ r = dns_name_is_valid(name);
if (r < 0)
+ return r;
+ if (r == 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid name '%s'", name);
- r = check_ifindex_flags(ifindex, &flags, error);
+ if (!dns_type_is_valid_query(type))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified resource record type %" PRIu16 " may not be used in a query.", type);
+ if (dns_type_is_obsolete(type))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Specified DNS resource record type %" PRIu16 " is obsolete.", type);
+
+ r = check_ifindex_flags(ifindex, &flags, 0, error);
if (r < 0)
return r;
@@ -618,32 +580,892 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd
if (r < 0)
return r;
- r = dns_query_new(m, &q, question, ifindex, flags);
+ r = dns_query_new(m, &q, question, question, ifindex, flags|SD_RESOLVED_NO_SEARCH);
if (r < 0)
return r;
q->request = sd_bus_message_ref(message);
- q->request_hostname = name;
q->complete = bus_method_resolve_record_complete;
r = dns_query_bus_track(q, message);
if (r < 0)
- return r;
+ goto fail;
r = dns_query_go(q);
+ if (r < 0)
+ goto fail;
+
+ return 1;
+
+fail:
+ dns_query_free(q);
+ return r;
+}
+
+static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL;
+ DnsQuery *aux;
+ int r;
+
+ assert(q);
+ assert(reply);
+ assert(rr);
+ assert(rr->key);
+
+ if (rr->key->type != DNS_TYPE_SRV)
+ return 0;
+
+ if ((q->flags & SD_RESOLVED_NO_ADDRESS) == 0) {
+ /* First, let's see if we could find an appropriate A or AAAA
+ * record for the SRV record */
+ LIST_FOREACH(auxiliary_queries, aux, q->auxiliary_queries) {
+ DnsResourceRecord *zz;
+ DnsQuestion *question;
+
+ if (aux->state != DNS_TRANSACTION_SUCCESS)
+ continue;
+ if (aux->auxiliary_result != 0)
+ continue;
+
+ question = dns_query_question_for_protocol(aux, aux->answer_protocol);
+
+ r = dns_name_equal(dns_question_first_name(question), rr->srv.name);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ DNS_ANSWER_FOREACH(zz, aux->answer) {
+
+ r = dns_question_matches_rr(question, zz, NULL);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ canonical = dns_resource_record_ref(zz);
+ break;
+ }
+
+ if (canonical)
+ break;
+ }
+
+ /* Is there are successful A/AAAA lookup for this SRV RR? If not, don't add it */
+ if (!canonical)
+ return 0;
+ }
+
+ r = sd_bus_message_open_container(reply, 'r', "qqqsa(iiay)s");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(
+ reply,
+ "qqqs",
+ rr->srv.priority, rr->srv.weight, rr->srv.port, rr->srv.name);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(reply, 'a', "(iiay)");
+ if (r < 0)
+ return r;
+
+ if ((q->flags & SD_RESOLVED_NO_ADDRESS) == 0) {
+ LIST_FOREACH(auxiliary_queries, aux, q->auxiliary_queries) {
+ DnsResourceRecord *zz;
+ DnsQuestion *question;
+ int ifindex;
+
+ if (aux->state != DNS_TRANSACTION_SUCCESS)
+ continue;
+ if (aux->auxiliary_result != 0)
+ continue;
+
+ question = dns_query_question_for_protocol(aux, aux->answer_protocol);
+
+ r = dns_name_equal(dns_question_first_name(question), rr->srv.name);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ DNS_ANSWER_FOREACH_IFINDEX(zz, ifindex, aux->answer) {
+
+ r = dns_question_matches_rr(question, zz, NULL);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ r = append_address(reply, zz, ifindex);
+ if (r < 0)
+ return r;
+ }
+ }
+ }
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+
+ /* Note that above we appended the hostname as encoded in the
+ * SRV, and here the canonical hostname this maps to. */
+ r = sd_bus_message_append(reply, "s", canonical ? DNS_RESOURCE_KEY_NAME(canonical->key) : rr->srv.name);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static int append_txt(sd_bus_message *reply, DnsResourceRecord *rr) {
+ DnsTxtItem *i;
+ int r;
+
+ assert(reply);
+ assert(rr);
+ assert(rr->key);
+
+ if (rr->key->type != DNS_TYPE_TXT)
+ return 0;
+
+ LIST_FOREACH(items, i, rr->txt.items) {
+
+ if (i->length <= 0)
+ continue;
+
+ r = sd_bus_message_append_array(reply, 'y', i->data, i->length);
+ if (r < 0)
+ return r;
+ }
+
+ return 1;
+}
+
+static void resolve_service_all_complete(DnsQuery *q) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_free_ char *name = NULL, *type = NULL, *domain = NULL;
+ DnsQuestion *question;
+ DnsResourceRecord *rr;
+ unsigned added = 0;
+ DnsQuery *aux;
+ int r;
+
+ assert(q);
+
+ if (q->block_all_complete > 0)
+ return;
+
+ if ((q->flags & SD_RESOLVED_NO_ADDRESS) == 0) {
+ DnsQuery *bad = NULL;
+ bool have_success = false;
+
+ LIST_FOREACH(auxiliary_queries, aux, q->auxiliary_queries) {
+
+ switch (aux->state) {
+
+ case DNS_TRANSACTION_PENDING:
+ /* If an auxiliary query is still pending, let's wait */
+ return;
+
+ case DNS_TRANSACTION_SUCCESS:
+ if (aux->auxiliary_result == 0)
+ have_success = true;
+ else
+ bad = aux;
+ break;
+
+ default:
+ bad = aux;
+ break;
+ }
+ }
+
+ if (!have_success) {
+ /* We can only return one error, hence pick the last error we encountered */
+
+ assert(bad);
+
+ if (bad->state == DNS_TRANSACTION_SUCCESS) {
+ assert(bad->auxiliary_result != 0);
+
+ if (bad->auxiliary_result == -ELOOP) {
+ r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(bad));
+ goto finish;
+ }
+
+ r = bad->auxiliary_result;
+ goto finish;
+ }
+
+ r = reply_query_state(bad);
+ goto finish;
+ }
+ }
+
+ r = sd_bus_message_new_method_return(q->request, &reply);
+ if (r < 0)
+ goto finish;
+
+ r = sd_bus_message_open_container(reply, 'a', "(qqqsa(iiay)s)");
+ if (r < 0)
+ goto finish;
+
+ question = dns_query_question_for_protocol(q, q->answer_protocol);
+ DNS_ANSWER_FOREACH(rr, q->answer) {
+ r = dns_question_matches_rr(question, rr, NULL);
+ if (r < 0)
+ goto finish;
+ if (r == 0)
+ continue;
+
+ r = append_srv(q, reply, rr);
+ if (r < 0)
+ goto finish;
+ if (r == 0) /* not an SRV record */
+ continue;
+
+ if (!canonical)
+ canonical = dns_resource_record_ref(rr);
+
+ added++;
+ }
+
+ if (added <= 0) {
+ r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_query_string(q));
+ goto finish;
+ }
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ goto finish;
+
+ r = sd_bus_message_open_container(reply, 'a', "ay");
+ if (r < 0)
+ goto finish;
+
+ DNS_ANSWER_FOREACH(rr, q->answer) {
+ r = dns_question_matches_rr(question, rr, NULL);
+ if (r < 0)
+ goto finish;
+ if (r == 0)
+ continue;
+
+ r = append_txt(reply, rr);
+ if (r < 0)
+ goto finish;
+ }
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ goto finish;
+
+ assert(canonical);
+ r = dns_service_split(DNS_RESOURCE_KEY_NAME(canonical->key), &name, &type, &domain);
+ if (r < 0)
+ goto finish;
+
+ r = sd_bus_message_append(
+ reply,
+ "ssst",
+ name, type, domain,
+ SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, q->answer_authenticated));
+ if (r < 0)
+ goto finish;
+
+ r = sd_bus_send(q->manager->bus, reply, NULL);
+
+finish:
if (r < 0) {
- dns_query_free(q);
+ log_error_errno(r, "Failed to send service reply: %m");
+ sd_bus_reply_method_errno(q->request, r, NULL);
+ }
+
+ dns_query_free(q);
+}
+
+static void resolve_service_hostname_complete(DnsQuery *q) {
+ int r;
+
+ assert(q);
+ assert(q->auxiliary_for);
+
+ if (q->state != DNS_TRANSACTION_SUCCESS) {
+ resolve_service_all_complete(q->auxiliary_for);
+ return;
+ }
+
+ r = dns_query_process_cname(q);
+ if (r == DNS_QUERY_RESTARTED) /* This was a cname, and the query was restarted. */
+ return;
+
+ /* This auxiliary lookup is finished or failed, let's see if all are finished now. */
+ q->auxiliary_result = r;
+ resolve_service_all_complete(q->auxiliary_for);
+}
+
+static int resolve_service_hostname(DnsQuery *q, DnsResourceRecord *rr, int ifindex) {
+ _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
+ DnsQuery *aux;
+ int r;
+
+ assert(q);
+ assert(rr);
+ assert(rr->key);
+ assert(rr->key->type == DNS_TYPE_SRV);
+
+ /* OK, we found an SRV record for the service. Let's resolve
+ * the hostname included in it */
+
+ r = dns_question_new_address(&question, q->request_family, rr->srv.name, false);
+ if (r < 0)
return r;
+
+ r = dns_query_new(q->manager, &aux, question, question, ifindex, q->flags|SD_RESOLVED_NO_SEARCH);
+ if (r < 0)
+ return r;
+
+ aux->request_family = q->request_family;
+ aux->complete = resolve_service_hostname_complete;
+
+ r = dns_query_make_auxiliary(aux, q);
+ if (r == -EAGAIN) {
+ /* Too many auxiliary lookups? If so, don't complain,
+ * let's just not add this one, we already have more
+ * than enough */
+
+ dns_query_free(aux);
+ return 0;
+ }
+ if (r < 0)
+ goto fail;
+
+ /* Note that auxiliary queries do not track the original bus
+ * client, only the primary request does that. */
+
+ r = dns_query_go(aux);
+ if (r < 0)
+ goto fail;
+
+ return 1;
+
+fail:
+ dns_query_free(aux);
+ return r;
+}
+
+static void bus_method_resolve_service_complete(DnsQuery *q) {
+ bool has_root_domain = false;
+ DnsResourceRecord *rr;
+ DnsQuestion *question;
+ unsigned found = 0;
+ int ifindex, r;
+
+ assert(q);
+
+ if (q->state != DNS_TRANSACTION_SUCCESS) {
+ r = reply_query_state(q);
+ goto finish;
+ }
+
+ r = dns_query_process_cname(q);
+ if (r == -ELOOP) {
+ r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q));
+ goto finish;
+ }
+ if (r < 0)
+ goto finish;
+ if (r == DNS_QUERY_RESTARTED) /* This was a cname, and the query was restarted. */
+ return;
+
+ question = dns_query_question_for_protocol(q, q->answer_protocol);
+
+ DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
+ r = dns_question_matches_rr(question, rr, NULL);
+ if (r < 0)
+ goto finish;
+ if (r == 0)
+ continue;
+
+ if (rr->key->type != DNS_TYPE_SRV)
+ continue;
+
+ if (dns_name_is_root(rr->srv.name)) {
+ has_root_domain = true;
+ continue;
+ }
+
+ if ((q->flags & SD_RESOLVED_NO_ADDRESS) == 0) {
+ q->block_all_complete ++;
+ r = resolve_service_hostname(q, rr, ifindex);
+ q->block_all_complete --;
+
+ if (r < 0)
+ goto finish;
+ }
+
+ found++;
+ }
+
+ if (has_root_domain && found <= 0) {
+ /* If there's exactly one SRV RR and it uses
+ * the root domain as host name, then the
+ * service is explicitly not offered on the
+ * domain. Report this as a recognizable
+ * error. See RFC 2782, Section "Usage
+ * Rules". */
+ r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_SERVICE, "'%s' does not provide the requested service", dns_query_string(q));
+ goto finish;
+ }
+
+ if (found <= 0) {
+ r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_query_string(q));
+ goto finish;
+ }
+
+ /* Maybe we are already finished? check now... */
+ resolve_service_all_complete(q);
+ return;
+
+finish:
+ if (r < 0) {
+ log_error_errno(r, "Failed to send service reply: %m");
+ sd_bus_reply_method_errno(q->request, r, NULL);
}
+ dns_query_free(q);
+}
+
+static int bus_method_resolve_service(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_(dns_question_unrefp) DnsQuestion *question_idna = NULL, *question_utf8 = NULL;
+ const char *name, *type, *domain;
+ _cleanup_free_ char *n = NULL;
+ Manager *m = userdata;
+ int family, ifindex;
+ uint64_t flags;
+ DnsQuery *q;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ assert_cc(sizeof(int) == sizeof(int32_t));
+
+ r = sd_bus_message_read(message, "isssit", &ifindex, &name, &type, &domain, &family, &flags);
+ if (r < 0)
+ return r;
+
+ if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
+
+ if (isempty(name))
+ name = NULL;
+ else if (!dns_service_name_is_valid(name))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid service name '%s'", name);
+
+ if (isempty(type))
+ type = NULL;
+ else if (!dns_srv_type_is_valid(type))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid SRV service type '%s'", type);
+
+ r = dns_name_is_valid(domain);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid domain '%s'", domain);
+
+ if (name && !type)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Service name cannot be specified without service type.");
+
+ r = check_ifindex_flags(ifindex, &flags, SD_RESOLVED_NO_TXT|SD_RESOLVED_NO_ADDRESS, error);
+ if (r < 0)
+ return r;
+
+ r = dns_question_new_service(&question_utf8, name, type, domain, !(flags & SD_RESOLVED_NO_TXT), false);
+ if (r < 0)
+ return r;
+
+ r = dns_question_new_service(&question_idna, name, type, domain, !(flags & SD_RESOLVED_NO_TXT), true);
+ if (r < 0)
+ return r;
+
+ r = dns_query_new(m, &q, question_utf8, question_idna, ifindex, flags|SD_RESOLVED_NO_SEARCH);
+ if (r < 0)
+ return r;
+
+ q->request = sd_bus_message_ref(message);
+ q->request_family = family;
+ q->complete = bus_method_resolve_service_complete;
+
+ r = dns_query_bus_track(q, message);
+ if (r < 0)
+ goto fail;
+
+ r = dns_query_go(q);
+ if (r < 0)
+ goto fail;
+
return 1;
+
+fail:
+ dns_query_free(q);
+ return r;
+}
+
+int bus_dns_server_append(sd_bus_message *reply, DnsServer *s, bool with_ifindex) {
+ int r;
+
+ assert(reply);
+ assert(s);
+
+ r = sd_bus_message_open_container(reply, 'r', with_ifindex ? "iiay" : "iay");
+ if (r < 0)
+ return r;
+
+ if (with_ifindex) {
+ r = sd_bus_message_append(reply, "i", s->link ? s->link->ifindex : 0);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_append(reply, "i", s->family);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append_array(reply, 'y', &s->address, FAMILY_ADDRESS_SIZE(s->family));
+ if (r < 0)
+ return r;
+
+ return sd_bus_message_close_container(reply);
+}
+
+static int bus_property_get_dns_servers(
+ 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;
+ unsigned c = 0;
+ DnsServer *s;
+ Iterator i;
+ Link *l;
+ int r;
+
+ assert(reply);
+ assert(m);
+
+ r = sd_bus_message_open_container(reply, 'a', "(iiay)");
+ if (r < 0)
+ return r;
+
+ LIST_FOREACH(servers, s, m->dns_servers) {
+ r = bus_dns_server_append(reply, s, true);
+ if (r < 0)
+ return r;
+
+ c++;
+ }
+
+ HASHMAP_FOREACH(l, m->links, i) {
+ LIST_FOREACH(servers, s, l->dns_servers) {
+ r = bus_dns_server_append(reply, s, true);
+ if (r < 0)
+ return r;
+ c++;
+ }
+ }
+
+ if (c == 0) {
+ LIST_FOREACH(servers, s, m->fallback_dns_servers) {
+ r = bus_dns_server_append(reply, s, true);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ return sd_bus_message_close_container(reply);
+}
+
+static int bus_property_get_search_domains(
+ 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;
+ DnsSearchDomain *d;
+ Iterator i;
+ Link *l;
+ int r;
+
+ assert(reply);
+ assert(m);
+
+ r = sd_bus_message_open_container(reply, 'a', "(is)");
+ if (r < 0)
+ return r;
+
+ LIST_FOREACH(domains, d, m->search_domains) {
+ r = sd_bus_message_append(reply, "(is)", 0, d->name);
+ if (r < 0)
+ return r;
+ }
+
+ HASHMAP_FOREACH(l, m->links, i) {
+ LIST_FOREACH(domains, d, l->search_domains) {
+ r = sd_bus_message_append(reply, "is", l->ifindex, d->name);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ return sd_bus_message_close_container(reply);
+}
+
+static int bus_property_get_transaction_statistics(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Manager *m = userdata;
+
+ assert(reply);
+ assert(m);
+
+ return sd_bus_message_append(reply, "(tt)",
+ (uint64_t) hashmap_size(m->dns_transactions),
+ (uint64_t) m->n_transactions_total);
+}
+
+static int bus_property_get_cache_statistics(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ uint64_t size = 0, hit = 0, miss = 0;
+ Manager *m = userdata;
+ DnsScope *s;
+
+ assert(reply);
+ assert(m);
+
+ LIST_FOREACH(scopes, s, m->dns_scopes) {
+ size += dns_cache_size(&s->cache);
+ hit += s->cache.n_hit;
+ miss += s->cache.n_miss;
+ }
+
+ return sd_bus_message_append(reply, "(ttt)", size, hit, miss);
+}
+
+static int bus_property_get_dnssec_statistics(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Manager *m = userdata;
+
+ assert(reply);
+ assert(m);
+
+ return sd_bus_message_append(reply, "(tttt)",
+ (uint64_t) m->n_dnssec_secure,
+ (uint64_t) m->n_dnssec_insecure,
+ (uint64_t) m->n_dnssec_bogus,
+ (uint64_t) m->n_dnssec_indeterminate);
+}
+
+static int bus_property_get_dnssec_supported(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Manager *m = userdata;
+
+ assert(reply);
+ assert(m);
+
+ return sd_bus_message_append(reply, "b", manager_dnssec_supported(m));
+}
+
+static int bus_method_reset_statistics(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ DnsScope *s;
+
+ assert(message);
+ assert(m);
+
+ LIST_FOREACH(scopes, s, m->dns_scopes)
+ s->cache.n_hit = s->cache.n_miss = 0;
+
+ m->n_transactions_total = 0;
+ m->n_dnssec_secure = m->n_dnssec_insecure = m->n_dnssec_bogus = m->n_dnssec_indeterminate = 0;
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+static int get_any_link(Manager *m, int ifindex, Link **ret, sd_bus_error *error) {
+ Link *l;
+
+ assert(m);
+ assert(ret);
+
+ if (ifindex <= 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
+
+ l = hashmap_get(m->links, INT_TO_PTR(ifindex));
+ if (!l)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_LINK, "Link %i not known", ifindex);
+
+ *ret = l;
+ return 0;
+}
+
+static int get_unmanaged_link(Manager *m, int ifindex, Link **ret, sd_bus_error *error) {
+ Link *l;
+ int r;
+
+ assert(m);
+ assert(ret);
+
+ r = get_any_link(m, ifindex, &l, error);
+ if (r < 0)
+ return r;
+
+ if (l->flags & IFF_LOOPBACK)
+ return sd_bus_error_setf(error, BUS_ERROR_LINK_BUSY, "Link %s is loopback device.", l->name);
+ if (l->is_managed)
+ return sd_bus_error_setf(error, BUS_ERROR_LINK_BUSY, "Link %s is managed.", l->name);
+
+ *ret = l;
+ return 0;
+}
+
+static int call_link_method(Manager *m, sd_bus_message *message, sd_bus_message_handler_t handler, sd_bus_error *error) {
+ int ifindex, r;
+ Link *l;
+
+ assert(m);
+ assert(message);
+ assert(handler);
+
+ assert_cc(sizeof(int) == sizeof(int32_t));
+ r = sd_bus_message_read(message, "i", &ifindex);
+ if (r < 0)
+ return r;
+
+ r = get_unmanaged_link(m, ifindex, &l, error);
+ if (r < 0)
+ return r;
+
+ return handler(message, l, error);
+}
+
+static int bus_method_set_link_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return call_link_method(userdata, message, bus_link_method_set_dns_servers, error);
+}
+
+static int bus_method_set_link_search_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return call_link_method(userdata, message, bus_link_method_set_search_domains, error);
+}
+
+static int bus_method_set_link_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return call_link_method(userdata, message, bus_link_method_set_llmnr, error);
+}
+
+static int bus_method_set_link_mdns(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return call_link_method(userdata, message, bus_link_method_set_mdns, error);
+}
+
+static int bus_method_set_link_dnssec(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return call_link_method(userdata, message, bus_link_method_set_dnssec, error);
+}
+
+static int bus_method_set_link_dnssec_negative_trust_anchors(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return call_link_method(userdata, message, bus_link_method_set_dnssec_negative_trust_anchors, error);
+}
+
+static int bus_method_revert_link(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return call_link_method(userdata, message, bus_link_method_revert, error);
+}
+
+static int bus_method_get_link(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_free_ char *p = NULL;
+ Manager *m = userdata;
+ int r, ifindex;
+ Link *l;
+
+ assert(message);
+ assert(m);
+
+ assert_cc(sizeof(int) == sizeof(int32_t));
+ r = sd_bus_message_read(message, "i", &ifindex);
+ if (r < 0)
+ return r;
+
+ r = get_any_link(m, ifindex, &l, error);
+ if (r < 0)
+ return r;
+
+ p = link_bus_path(l);
+ if (!p)
+ return -ENOMEM;
+
+ return sd_bus_reply_method_return(message, "o", p);
}
static const sd_bus_vtable resolve_vtable[] = {
SD_BUS_VTABLE_START(0),
+ SD_BUS_PROPERTY("LLMNRHostname", "s", NULL, offsetof(Manager, llmnr_hostname), 0),
+ SD_BUS_PROPERTY("DNS", "a(iiay)", bus_property_get_dns_servers, 0, 0),
+ SD_BUS_PROPERTY("Domains", "a(is)", bus_property_get_search_domains, 0, 0),
+ SD_BUS_PROPERTY("TransactionStatistics", "(tt)", bus_property_get_transaction_statistics, 0, 0),
+ SD_BUS_PROPERTY("CacheStatistics", "(ttt)", bus_property_get_cache_statistics, 0, 0),
+ SD_BUS_PROPERTY("DNSSECStatistics", "(tttt)", bus_property_get_dnssec_statistics, 0, 0),
+ SD_BUS_PROPERTY("DNSSECSupported", "b", bus_property_get_dnssec_supported, 0, 0),
+
SD_BUS_METHOD("ResolveHostname", "isit", "a(iiay)st", bus_method_resolve_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ResolveAddress", "iiayt", "a(is)t", bus_method_resolve_address, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ResolveRecord", "isqqt", "a(iqqay)t", bus_method_resolve_record, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("ResolveService", "isssit", "a(qqqsa(iiay)s)aayssst", bus_method_resolve_service, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("ResetStatistics", NULL, NULL, bus_method_reset_statistics, 0),
+ SD_BUS_METHOD("GetLink", "i", "o", bus_method_get_link, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SetLinkDNS", "ia(iay)", NULL, bus_method_set_link_dns_servers, 0),
+ SD_BUS_METHOD("SetLinkDomains", "ias", NULL, bus_method_set_link_search_domains, 0),
+ SD_BUS_METHOD("SetLinkLLMNR", "is", NULL, bus_method_set_link_llmnr, 0),
+ SD_BUS_METHOD("SetLinkMulticastDNS", "is", NULL, bus_method_set_link_mdns, 0),
+ SD_BUS_METHOD("SetLinkDNSSEC", "is", NULL, bus_method_set_link_dnssec, 0),
+ SD_BUS_METHOD("SetLinkDNSSECNegativeTrustAnchors", "ias", NULL, bus_method_set_link_dnssec_negative_trust_anchors, 0),
+ SD_BUS_METHOD("RevertLink", "i", NULL, bus_method_revert_link, 0),
+
SD_BUS_VTABLE_END,
};
@@ -701,6 +1523,7 @@ int manager_connect_bus(Manager *m) {
if (r < 0)
return log_error_errno(r, "Failed to install bus reconnect time event: %m");
+ (void) sd_event_source_set_description(m->bus_retry_event_source, "bus-retry");
return 0;
}
@@ -708,6 +1531,14 @@ int manager_connect_bus(Manager *m) {
if (r < 0)
return log_error_errno(r, "Failed to register object: %m");
+ r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/resolve1/link", "org.freedesktop.resolve1.Link", link_vtable, link_object_find, m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to register link objects: %m");
+
+ r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/resolve1/link", link_node_enumerator, m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to register link enumerator: %m");
+
r = sd_bus_request_name(m->bus, "org.freedesktop.resolve1", 0);
if (r < 0)
return log_error_errno(r, "Failed to register name: %m");
diff --git a/src/resolve/resolved-bus.h b/src/resolve/resolved-bus.h
index 1e72891178..1ee57ba43d 100644
--- a/src/resolve/resolved-bus.h
+++ b/src/resolve/resolved-bus.h
@@ -24,3 +24,4 @@
#include "resolved-manager.h"
int manager_connect_bus(Manager *m);
+int bus_dns_server_append(sd_bus_message *reply, DnsServer *s, bool with_ifindex);
diff --git a/src/resolve/resolved-conf.c b/src/resolve/resolved-conf.c
index 7af63b0a82..88df7534c4 100644
--- a/src/resolve/resolved-conf.c
+++ b/src/resolve/resolved-conf.c
@@ -19,56 +19,107 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "alloc-util.h"
#include "conf-parser.h"
-
+#include "def.h"
+#include "extract-word.h"
+#include "parse-util.h"
#include "resolved-conf.h"
+#include "string-util.h"
+
+int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char *word) {
+ union in_addr_union address;
+ int family, r;
+ DnsServer *s;
+
+ assert(m);
+ assert(word);
+
+ r = in_addr_from_string_auto(word, &family, &address);
+ if (r < 0)
+ return r;
+
+ /* Filter out duplicates */
+ s = dns_server_find(manager_get_first_dns_server(m, type), family, &address);
+ if (s) {
+ /*
+ * Drop the marker. This is used to find the servers
+ * that ceased to exist, see
+ * manager_mark_dns_servers() and
+ * manager_flush_marked_dns_servers().
+ */
+ dns_server_move_back_and_unmark(s);
+ return 0;
+ }
+
+ return dns_server_new(m, NULL, type, NULL, family, &address);
+}
-int manager_parse_dns_server(Manager *m, DnsServerType type, const char *string) {
- const char *word, *state;
- size_t length;
- DnsServer *first;
+int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string) {
int r;
assert(m);
assert(string);
- first = type == DNS_SERVER_FALLBACK ? m->fallback_dns_servers : m->dns_servers;
+ for(;;) {
+ _cleanup_free_ char *word = NULL;
- FOREACH_WORD_QUOTED(word, length, string, state) {
- char buffer[length+1];
- int family;
- union in_addr_union addr;
- bool found = false;
- DnsServer *s;
+ r = extract_first_word(&string, &word, NULL, 0);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
- memcpy(buffer, word, length);
- buffer[length] = 0;
+ r = manager_add_dns_server_by_string(m, type, word);
+ if (r < 0)
+ log_warning_errno(r, "Failed to add DNS server address '%s', ignoring.", word);
+ }
- r = in_addr_from_string_auto(buffer, &family, &addr);
- if (r < 0) {
- log_warning("Ignoring invalid DNS address '%s'", buffer);
- continue;
- }
+ return 0;
+}
+
+int manager_add_search_domain_by_string(Manager *m, const char *domain) {
+ DnsSearchDomain *d;
+ int r;
+
+ assert(m);
+ assert(domain);
+
+ r = dns_search_domain_find(m->search_domains, domain, &d);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ dns_search_domain_move_back_and_unmark(d);
+ return 0;
+ }
+
+ return dns_search_domain_new(m, NULL, DNS_SEARCH_DOMAIN_SYSTEM, NULL, domain);
+}
+
+int manager_parse_search_domains_and_warn(Manager *m, const char *string) {
+ int r;
- /* Filter out duplicates */
- LIST_FOREACH(servers, s, first)
- if (s->family == family && in_addr_equal(family, &s->address, &addr)) {
- found = true;
- break;
- }
+ assert(m);
+ assert(string);
- if (found)
- continue;
+ for(;;) {
+ _cleanup_free_ char *word = NULL;
- r = dns_server_new(m, NULL, type, NULL, family, &addr);
+ r = extract_first_word(&string, &word, NULL, EXTRACT_QUOTES);
if (r < 0)
return r;
+ if (r == 0)
+ break;
+
+ r = manager_add_search_domain_by_string(m, word);
+ if (r < 0)
+ log_warning_errno(r, "Failed to add search domain '%s', ignoring.", word);
}
return 0;
}
-int config_parse_dnsv(
+int config_parse_dns_servers(
const char *unit,
const char *filename,
unsigned line,
@@ -90,12 +141,12 @@ int config_parse_dnsv(
if (isempty(rvalue))
/* Empty assignment means clear the list */
- manager_flush_dns_servers(m, ltype);
+ dns_server_unlink_all(manager_get_first_dns_server(m, ltype));
else {
- /* Otherwise add to the list */
- r = manager_parse_dns_server(m, ltype, rvalue);
+ /* Otherwise, add to the list */
+ r = manager_parse_dns_server_string_and_warn(m, ltype, rvalue);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to parse DNS server string '%s'. Ignoring.", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse DNS server string '%s'. Ignoring.", rvalue);
return 0;
}
}
@@ -104,11 +155,13 @@ int config_parse_dnsv(
* /etc/resolv.conf */
if (ltype == DNS_SERVER_SYSTEM)
m->read_resolv_conf = false;
+ if (ltype == DNS_SERVER_FALLBACK)
+ m->need_builtin_fallbacks = false;
return 0;
}
-int config_parse_support(
+int config_parse_search_domains(
const char *unit,
const char *filename,
unsigned line,
@@ -120,34 +173,52 @@ int config_parse_support(
void *data,
void *userdata) {
- Support support, *v = data;
+ Manager *m = userdata;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
+ assert(m);
- support = support_from_string(rvalue);
- if (support < 0) {
- r = parse_boolean(rvalue);
+ if (isempty(rvalue))
+ /* Empty assignment means clear the list */
+ dns_search_domain_unlink_all(m->search_domains);
+ else {
+ /* Otherwise, add to the list */
+ r = manager_parse_search_domains_and_warn(m, rvalue);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to parse support level '%s'. Ignoring.", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse search domains string '%s'. Ignoring.", rvalue);
return 0;
}
-
- support = r ? SUPPORT_YES : SUPPORT_NO;
}
- *v = support;
+ /* If we have a manual setting, then we stop reading
+ * /etc/resolv.conf */
+ m->read_resolv_conf = false;
+
return 0;
}
int manager_parse_config_file(Manager *m) {
+ int r;
+
assert(m);
- return config_parse_many("/etc/systemd/resolved.conf",
- CONF_DIRS_NULSTR("systemd/resolved.conf"),
- "Resolve\0",
- config_item_perf_lookup, resolved_gperf_lookup,
- false, m);
+ r = config_parse_many(PKGSYSCONFDIR "/resolved.conf",
+ CONF_PATHS_NULSTR("systemd/resolved.conf.d"),
+ "Resolve\0",
+ config_item_perf_lookup, resolved_gperf_lookup,
+ false, m);
+ if (r < 0)
+ return r;
+
+ if (m->need_builtin_fallbacks) {
+ r = manager_parse_dns_server_string_and_warn(m, DNS_SERVER_FALLBACK, DNS_SERVERS);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+
}
diff --git a/src/resolve/resolved-conf.h b/src/resolve/resolved-conf.h
index b3dbea7b6b..b4ef1b0378 100644
--- a/src/resolve/resolved-conf.h
+++ b/src/resolve/resolved-conf.h
@@ -23,10 +23,16 @@
#include "resolved-manager.h"
-int manager_parse_dns_server(Manager *m, DnsServerType type, const char *string);
int manager_parse_config_file(Manager *m);
+int manager_add_search_domain_by_string(Manager *m, const char *domain);
+int manager_parse_search_domains_and_warn(Manager *m, const char *string);
+
+int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char *word);
+int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string);
+
const struct ConfigPerfItem* resolved_gperf_lookup(const char *key, unsigned length);
-int config_parse_dnsv(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_support(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_dns_servers(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_search_domains(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_dnssec(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/resolve/resolved-def.h b/src/resolve/resolved-def.h
index 086d111205..6014d345f3 100644
--- a/src/resolve/resolved-def.h
+++ b/src/resolve/resolved-def.h
@@ -21,10 +21,18 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#define SD_RESOLVED_DNS ((uint64_t) 1)
-#define SD_RESOLVED_LLMNR_IPV4 ((uint64_t) 2)
-#define SD_RESOLVED_LLMNR_IPV6 ((uint64_t) 4)
+#define SD_RESOLVED_DNS (UINT64_C(1) << 0)
+#define SD_RESOLVED_LLMNR_IPV4 (UINT64_C(1) << 1)
+#define SD_RESOLVED_LLMNR_IPV6 (UINT64_C(1) << 2)
+#define SD_RESOLVED_MDNS_IPV4 (UINT64_C(1) << 3)
+#define SD_RESOLVED_MDNS_IPV6 (UINT64_C(1) << 4)
+#define SD_RESOLVED_NO_CNAME (UINT64_C(1) << 5)
+#define SD_RESOLVED_NO_TXT (UINT64_C(1) << 6)
+#define SD_RESOLVED_NO_ADDRESS (UINT64_C(1) << 7)
+#define SD_RESOLVED_NO_SEARCH (UINT64_C(1) << 8)
+#define SD_RESOLVED_AUTHENTICATED (UINT64_C(1) << 9)
+
#define SD_RESOLVED_LLMNR (SD_RESOLVED_LLMNR_IPV4|SD_RESOLVED_LLMNR_IPV6)
+#define SD_RESOLVED_MDNS (SD_RESOLVED_MDNS_IPV4|SD_RESOLVED_MDNS_IPV6)
-#define SD_RESOLVED_FLAGS_ALL (SD_RESOLVED_DNS|SD_RESOLVED_LLMNR_IPV4|SD_RESOLVED_LLMNR_IPV6)
-#define SD_RESOLVED_FLAGS_DEFAULT SD_RESOLVED_FLAGS_ALL
+#define SD_RESOLVED_PROTOCOLS_ALL (SD_RESOLVED_MDNS|SD_RESOLVED_LLMNR|SD_RESOLVED_DNS)
diff --git a/src/resolve/resolved-dns-answer.c b/src/resolve/resolved-dns-answer.c
index 89b9b0e1ea..f74e440531 100644
--- a/src/resolve/resolved-dns-answer.c
+++ b/src/resolve/resolved-dns-answer.c
@@ -19,8 +19,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "resolved-dns-answer.h"
+#include "alloc-util.h"
#include "dns-domain.h"
+#include "resolved-dns-answer.h"
+#include "resolved-dns-dnssec.h"
+#include "string-util.h"
DnsAnswer *dns_answer_new(unsigned n) {
DnsAnswer *a;
@@ -44,6 +47,18 @@ DnsAnswer *dns_answer_ref(DnsAnswer *a) {
return a;
}
+static void dns_answer_flush(DnsAnswer *a) {
+ DnsResourceRecord *rr;
+
+ if (!a)
+ return;
+
+ DNS_ANSWER_FOREACH(rr, a)
+ dns_resource_record_unref(rr);
+
+ a->n_rrs = 0;
+}
+
DnsAnswer *dns_answer_unref(DnsAnswer *a) {
if (!a)
return NULL;
@@ -51,11 +66,7 @@ DnsAnswer *dns_answer_unref(DnsAnswer *a) {
assert(a->n_ref > 0);
if (a->n_ref == 1) {
- unsigned i;
-
- for (i = 0; i < a->n_rrs; i++)
- dns_resource_record_unref(a->items[i].rr);
-
+ dns_answer_flush(a);
free(a);
} else
a->n_ref--;
@@ -63,7 +74,39 @@ DnsAnswer *dns_answer_unref(DnsAnswer *a) {
return NULL;
}
-int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex) {
+static int dns_answer_add_raw(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
+ assert(rr);
+
+ if (!a)
+ return -ENOSPC;
+
+ if (a->n_rrs >= a->n_allocated)
+ return -ENOSPC;
+
+ a->items[a->n_rrs++] = (DnsAnswerItem) {
+ .rr = dns_resource_record_ref(rr),
+ .ifindex = ifindex,
+ .flags = flags,
+ };
+
+ return 1;
+}
+
+static int dns_answer_add_raw_all(DnsAnswer *a, DnsAnswer *source) {
+ DnsResourceRecord *rr;
+ DnsAnswerFlags flags;
+ int ifindex, r;
+
+ DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, source) {
+ r = dns_answer_add_raw(a, rr, ifindex, flags);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
unsigned i;
int r;
@@ -71,6 +114,8 @@ int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex) {
if (!a)
return -ENOSPC;
+ if (a->n_ref > 1)
+ return -EBUSY;
for (i = 0; i < a->n_rrs; i++) {
if (a->items[i].ifindex != ifindex)
@@ -80,27 +125,66 @@ int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex) {
if (r < 0)
return r;
if (r > 0) {
- /* Entry already exists, keep the entry with
- * the higher RR, or the one with TTL 0 */
+ /* Don't mix contradicting TTLs (see below) */
+ if ((rr->ttl == 0) != (a->items[i].rr->ttl == 0))
+ return -EINVAL;
- if (rr->ttl == 0 || (rr->ttl > a->items[i].rr->ttl && a->items[i].rr->ttl != 0)) {
+ /* Entry already exists, keep the entry with
+ * the higher RR. */
+ if (rr->ttl > a->items[i].rr->ttl) {
dns_resource_record_ref(rr);
dns_resource_record_unref(a->items[i].rr);
a->items[i].rr = rr;
}
+ a->items[i].flags |= flags;
return 0;
}
+
+ r = dns_resource_key_equal(a->items[i].rr->key, rr->key);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ /* There's already an RR of the same RRset in
+ * place! Let's see if the TTLs more or less
+ * match. We don't really care if they match
+ * precisely, but we do care whether one is 0
+ * and the other is not. See RFC 2181, Section
+ * 5.2.*/
+
+ if ((rr->ttl == 0) != (a->items[i].rr->ttl == 0))
+ return -EINVAL;
+ }
}
- if (a->n_rrs >= a->n_allocated)
- return -ENOSPC;
+ return dns_answer_add_raw(a, rr, ifindex, flags);
+}
- a->items[a->n_rrs].rr = dns_resource_record_ref(rr);
- a->items[a->n_rrs].ifindex = ifindex;
- a->n_rrs++;
+static int dns_answer_add_all(DnsAnswer *a, DnsAnswer *b) {
+ DnsResourceRecord *rr;
+ DnsAnswerFlags flags;
+ int ifindex, r;
- return 1;
+ DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, b) {
+ r = dns_answer_add(a, rr, ifindex, flags);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int dns_answer_add_extend(DnsAnswer **a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
+ int r;
+
+ assert(a);
+ assert(rr);
+
+ r = dns_answer_reserve_or_clone(a, 1);
+ if (r < 0)
+ return r;
+
+ return dns_answer_add(*a, rr, ifindex, flags);
}
int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl) {
@@ -126,101 +210,476 @@ int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl) {
soa->soa.expire = 1;
soa->soa.minimum = ttl;
- return dns_answer_add(a, soa, 0);
+ return dns_answer_add(a, soa, 0, DNS_ANSWER_AUTHENTICATED);
}
-int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key) {
- unsigned i;
+int dns_answer_match_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *ret_flags) {
+ DnsAnswerFlags flags = 0, i_flags;
+ DnsResourceRecord *i;
+ bool found = false;
int r;
assert(key);
- if (!a)
- return 0;
+ DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
+ r = dns_resource_key_match_rr(key, i, NULL);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
- for (i = 0; i < a->n_rrs; i++) {
- r = dns_resource_key_match_rr(key, a->items[i].rr);
+ if (!ret_flags)
+ return 1;
+
+ if (found)
+ flags &= i_flags;
+ else {
+ flags = i_flags;
+ found = true;
+ }
+ }
+
+ if (ret_flags)
+ *ret_flags = flags;
+
+ return found;
+}
+
+int dns_answer_contains_rr(DnsAnswer *a, DnsResourceRecord *rr, DnsAnswerFlags *ret_flags) {
+ DnsAnswerFlags flags = 0, i_flags;
+ DnsResourceRecord *i;
+ bool found = false;
+ int r;
+
+ assert(rr);
+
+ DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
+ r = dns_resource_record_equal(i, rr);
if (r < 0)
return r;
- if (r > 0)
+ if (r == 0)
+ continue;
+
+ if (!ret_flags)
return 1;
+
+ if (found)
+ flags &= i_flags;
+ else {
+ flags = i_flags;
+ found = true;
+ }
}
- return 0;
+ if (ret_flags)
+ *ret_flags = flags;
+
+ return found;
}
-int dns_answer_match_soa(DnsResourceKey *key, DnsResourceKey *soa) {
- if (soa->class != DNS_CLASS_IN)
- return 0;
+int dns_answer_contains_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *ret_flags) {
+ DnsAnswerFlags flags = 0, i_flags;
+ DnsResourceRecord *i;
+ bool found = false;
+ int r;
+
+ assert(key);
+
+ DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
+ r = dns_resource_key_equal(i->key, key);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ if (!ret_flags)
+ return true;
+
+ if (found)
+ flags &= i_flags;
+ else {
+ flags = i_flags;
+ found = true;
+ }
+ }
+
+ if (ret_flags)
+ *ret_flags = flags;
+
+ return found;
+}
+
+int dns_answer_contains_nsec_or_nsec3(DnsAnswer *a) {
+ DnsResourceRecord *i;
+
+ DNS_ANSWER_FOREACH(i, a) {
+ if (IN_SET(i->key->type, DNS_TYPE_NSEC, DNS_TYPE_NSEC3))
+ return true;
+ }
+
+ return false;
+}
+
+int dns_answer_contains_zone_nsec3(DnsAnswer *answer, const char *zone) {
+ DnsResourceRecord *rr;
+ int r;
+
+ /* Checks whether the specified answer contains at least one NSEC3 RR in the specified zone */
+
+ DNS_ANSWER_FOREACH(rr, answer) {
+ const char *p;
+
+ if (rr->key->type != DNS_TYPE_NSEC3)
+ continue;
+
+ p = DNS_RESOURCE_KEY_NAME(rr->key);
+ r = dns_name_parent(&p);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ r = dns_name_equal(p, zone);
+ if (r != 0)
+ return r;
+ }
+
+ return false;
+}
+
+int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
+ DnsResourceRecord *rr, *soa = NULL;
+ DnsAnswerFlags rr_flags, soa_flags = 0;
+ int r;
+
+ assert(key);
- if (soa->type != DNS_TYPE_SOA)
+ /* For a SOA record we can never find a matching SOA record */
+ if (key->type == DNS_TYPE_SOA)
return 0;
- if (!dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(soa)))
+ DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
+ r = dns_resource_key_match_soa(key, rr->key);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+
+ if (soa) {
+ r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(soa->key));
+ if (r < 0)
+ return r;
+ if (r > 0)
+ continue;
+ }
+
+ soa = rr;
+ soa_flags = rr_flags;
+ }
+ }
+
+ if (!soa)
return 0;
+ if (ret)
+ *ret = soa;
+ if (flags)
+ *flags = soa_flags;
+
return 1;
}
-int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **ret) {
- unsigned i;
+int dns_answer_find_cname_or_dname(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
+ DnsResourceRecord *rr;
+ DnsAnswerFlags rr_flags;
+ int r;
assert(key);
+
+ /* For a {C,D}NAME record we can never find a matching {C,D}NAME record */
+ if (!dns_type_may_redirect(key->type))
+ return 0;
+
+ DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
+ r = dns_resource_key_match_cname_or_dname(key, rr->key, NULL);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ if (ret)
+ *ret = rr;
+ if (flags)
+ *flags = rr_flags;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int dns_answer_merge(DnsAnswer *a, DnsAnswer *b, DnsAnswer **ret) {
+ _cleanup_(dns_answer_unrefp) DnsAnswer *k = NULL;
+ int r;
+
assert(ret);
- if (!a)
+ if (dns_answer_size(a) <= 0) {
+ *ret = dns_answer_ref(b);
return 0;
+ }
- /* For a SOA record we can never find a matching SOA record */
- if (key->type == DNS_TYPE_SOA)
+ if (dns_answer_size(b) <= 0) {
+ *ret = dns_answer_ref(a);
return 0;
+ }
- for (i = 0; i < a->n_rrs; i++) {
+ k = dns_answer_new(a->n_rrs + b->n_rrs);
+ if (!k)
+ return -ENOMEM;
- if (dns_answer_match_soa(key, a->items[i].rr->key)) {
- *ret = a->items[i].rr;
- return 1;
- }
- }
+ r = dns_answer_add_raw_all(k, a);
+ if (r < 0)
+ return r;
+
+ r = dns_answer_add_all(k, b);
+ if (r < 0)
+ return r;
+
+ *ret = k;
+ k = NULL;
+
+ return 0;
+}
+
+int dns_answer_extend(DnsAnswer **a, DnsAnswer *b) {
+ DnsAnswer *merged;
+ int r;
+
+ assert(a);
+
+ r = dns_answer_merge(*a, b, &merged);
+ if (r < 0)
+ return r;
+
+ dns_answer_unref(*a);
+ *a = merged;
return 0;
}
-DnsAnswer *dns_answer_merge(DnsAnswer *a, DnsAnswer *b) {
- _cleanup_(dns_answer_unrefp) DnsAnswer *ret = NULL;
- DnsAnswer *k;
+int dns_answer_remove_by_key(DnsAnswer **a, const DnsResourceKey *key) {
+ bool found = false, other = false;
+ DnsResourceRecord *rr;
unsigned i;
int r;
- if (a && (!b || b->n_rrs <= 0))
- return dns_answer_ref(a);
- if ((!a || a->n_rrs <= 0) && b)
- return dns_answer_ref(b);
+ assert(a);
+ assert(key);
- ret = dns_answer_new((a ? a->n_rrs : 0) + (b ? b->n_rrs : 0));
- if (!ret)
- return NULL;
+ /* Remove all entries matching the specified key from *a */
+
+ DNS_ANSWER_FOREACH(rr, *a) {
+ r = dns_resource_key_equal(rr->key, key);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ found = true;
+ else
+ other = true;
+
+ if (found && other)
+ break;
+ }
+
+ if (!found)
+ return 0;
+
+ if (!other) {
+ *a = dns_answer_unref(*a); /* Return NULL for the empty answer */
+ return 1;
+ }
+
+ if ((*a)->n_ref > 1) {
+ _cleanup_(dns_answer_unrefp) DnsAnswer *copy = NULL;
+ DnsAnswerFlags flags;
+ int ifindex;
+
+ copy = dns_answer_new((*a)->n_rrs);
+ if (!copy)
+ return -ENOMEM;
+
+ DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, *a) {
+ r = dns_resource_key_equal(rr->key, key);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ continue;
- if (a) {
- for (i = 0; i < a->n_rrs; i++) {
- r = dns_answer_add(ret, a->items[i].rr, a->items[i].ifindex);
+ r = dns_answer_add_raw(copy, rr, ifindex, flags);
if (r < 0)
- return NULL;
+ return r;
}
+
+ dns_answer_unref(*a);
+ *a = copy;
+ copy = NULL;
+
+ return 1;
}
- if (b) {
- for (i = 0; i < b->n_rrs; i++) {
- r = dns_answer_add(ret, b->items[i].rr, b->items[i].ifindex);
+ /* Only a single reference, edit in-place */
+
+ i = 0;
+ for (;;) {
+ if (i >= (*a)->n_rrs)
+ break;
+
+ r = dns_resource_key_equal((*a)->items[i].rr->key, key);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ /* Kill this entry */
+
+ dns_resource_record_unref((*a)->items[i].rr);
+ memmove((*a)->items + i, (*a)->items + i + 1, sizeof(DnsAnswerItem) * ((*a)->n_rrs - i - 1));
+ (*a)->n_rrs --;
+ continue;
+
+ } else
+ /* Keep this entry */
+ i++;
+ }
+
+ return 1;
+}
+
+int dns_answer_remove_by_rr(DnsAnswer **a, DnsResourceRecord *rm) {
+ bool found = false, other = false;
+ DnsResourceRecord *rr;
+ unsigned i;
+ int r;
+
+ assert(a);
+ assert(rm);
+
+ /* Remove all entries matching the specified RR from *a */
+
+ DNS_ANSWER_FOREACH(rr, *a) {
+ r = dns_resource_record_equal(rr, rm);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ found = true;
+ else
+ other = true;
+
+ if (found && other)
+ break;
+ }
+
+ if (!found)
+ return 0;
+
+ if (!other) {
+ *a = dns_answer_unref(*a); /* Return NULL for the empty answer */
+ return 1;
+ }
+
+ if ((*a)->n_ref > 1) {
+ _cleanup_(dns_answer_unrefp) DnsAnswer *copy = NULL;
+ DnsAnswerFlags flags;
+ int ifindex;
+
+ copy = dns_answer_new((*a)->n_rrs);
+ if (!copy)
+ return -ENOMEM;
+
+ DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, *a) {
+ r = dns_resource_record_equal(rr, rm);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ continue;
+
+ r = dns_answer_add_raw(copy, rr, ifindex, flags);
if (r < 0)
- return NULL;
+ return r;
}
+
+ dns_answer_unref(*a);
+ *a = copy;
+ copy = NULL;
+
+ return 1;
}
- k = ret;
- ret = NULL;
+ /* Only a single reference, edit in-place */
- return k;
+ i = 0;
+ for (;;) {
+ if (i >= (*a)->n_rrs)
+ break;
+
+ r = dns_resource_record_equal((*a)->items[i].rr, rm);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ /* Kill this entry */
+
+ dns_resource_record_unref((*a)->items[i].rr);
+ memmove((*a)->items + i, (*a)->items + i + 1, sizeof(DnsAnswerItem) * ((*a)->n_rrs - i - 1));
+ (*a)->n_rrs --;
+ continue;
+
+ } else
+ /* Keep this entry */
+ i++;
+ }
+
+ return 1;
+}
+
+int dns_answer_copy_by_key(DnsAnswer **a, DnsAnswer *source, const DnsResourceKey *key, DnsAnswerFlags or_flags) {
+ DnsResourceRecord *rr_source;
+ int ifindex_source, r;
+ DnsAnswerFlags flags_source;
+
+ assert(a);
+ assert(key);
+
+ /* Copy all RRs matching the specified key from source into *a */
+
+ DNS_ANSWER_FOREACH_FULL(rr_source, ifindex_source, flags_source, source) {
+
+ r = dns_resource_key_equal(rr_source->key, key);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ /* Make space for at least one entry */
+ r = dns_answer_reserve_or_clone(a, 1);
+ if (r < 0)
+ return r;
+
+ r = dns_answer_add(*a, rr_source, ifindex_source, flags_source|or_flags);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int dns_answer_move_by_key(DnsAnswer **to, DnsAnswer **from, const DnsResourceKey *key, DnsAnswerFlags or_flags) {
+ int r;
+
+ assert(to);
+ assert(from);
+ assert(key);
+
+ r = dns_answer_copy_by_key(to, *from, key, or_flags);
+ if (r < 0)
+ return r;
+
+ return dns_answer_remove_by_key(from, key);
}
void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) {
@@ -259,6 +718,8 @@ void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) {
int dns_answer_reserve(DnsAnswer **a, unsigned n_free) {
DnsAnswer *n;
+ assert(a);
+
if (n_free <= 0)
return 0;
@@ -273,6 +734,9 @@ int dns_answer_reserve(DnsAnswer **a, unsigned n_free) {
if ((*a)->n_allocated >= ns)
return 0;
+ /* Allocate more than we need */
+ ns *= 2;
+
n = realloc(*a, offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * ns);
if (!n)
return -ENOMEM;
@@ -287,3 +751,110 @@ int dns_answer_reserve(DnsAnswer **a, unsigned n_free) {
*a = n;
return 0;
}
+
+int dns_answer_reserve_or_clone(DnsAnswer **a, unsigned n_free) {
+ _cleanup_(dns_answer_unrefp) DnsAnswer *n = NULL;
+ int r;
+
+ assert(a);
+
+ /* Tries to extend the DnsAnswer object. And if that's not
+ * possibly, since we are not the sole owner, then allocate a
+ * new, appropriately sized one. Either way, after this call
+ * the object will only have a single reference, and has room
+ * for at least the specified number of RRs. */
+
+ r = dns_answer_reserve(a, n_free);
+ if (r != -EBUSY)
+ return r;
+
+ assert(*a);
+
+ n = dns_answer_new(((*a)->n_rrs + n_free) * 2);
+ if (!n)
+ return -ENOMEM;
+
+ r = dns_answer_add_raw_all(n, *a);
+ if (r < 0)
+ return r;
+
+ dns_answer_unref(*a);
+ *a = n;
+ n = NULL;
+
+ return 0;
+}
+
+void dns_answer_dump(DnsAnswer *answer, FILE *f) {
+ DnsResourceRecord *rr;
+ DnsAnswerFlags flags;
+ int ifindex;
+
+ if (!f)
+ f = stdout;
+
+ DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, answer) {
+ const char *t;
+
+ fputc('\t', f);
+
+ t = dns_resource_record_to_string(rr);
+ if (!t) {
+ log_oom();
+ continue;
+ }
+
+ fputs(t, f);
+
+ if (ifindex != 0 || flags & (DNS_ANSWER_AUTHENTICATED|DNS_ANSWER_CACHEABLE|DNS_ANSWER_SHARED_OWNER))
+ fputs("\t;", f);
+
+ if (ifindex != 0)
+ printf(" ifindex=%i", ifindex);
+ if (flags & DNS_ANSWER_AUTHENTICATED)
+ fputs(" authenticated", f);
+ if (flags & DNS_ANSWER_CACHEABLE)
+ fputs(" cachable", f);
+ if (flags & DNS_ANSWER_SHARED_OWNER)
+ fputs(" shared-owner", f);
+
+ fputc('\n', f);
+ }
+}
+
+bool dns_answer_has_dname_for_cname(DnsAnswer *a, DnsResourceRecord *cname) {
+ DnsResourceRecord *rr;
+ int r;
+
+ assert(cname);
+
+ /* Checks whether the answer contains a DNAME record that indicates that the specified CNAME record is
+ * synthesized from it */
+
+ if (cname->key->type != DNS_TYPE_CNAME)
+ return 0;
+
+ DNS_ANSWER_FOREACH(rr, a) {
+ _cleanup_free_ char *n = NULL;
+
+ if (rr->key->type != DNS_TYPE_DNAME)
+ continue;
+ if (rr->key->class != cname->key->class)
+ continue;
+
+ r = dns_name_change_suffix(cname->cname.name, rr->dname.name, DNS_RESOURCE_KEY_NAME(rr->key), &n);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ r = dns_name_equal(n, DNS_RESOURCE_KEY_NAME(cname->key));
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return 1;
+
+ }
+
+ return 0;
+}
diff --git a/src/resolve/resolved-dns-answer.h b/src/resolve/resolved-dns-answer.h
index 044d73b19c..1875fd6136 100644
--- a/src/resolve/resolved-dns-answer.h
+++ b/src/resolve/resolved-dns-answer.h
@@ -24,16 +24,26 @@
typedef struct DnsAnswer DnsAnswer;
typedef struct DnsAnswerItem DnsAnswerItem;
+#include "macro.h"
#include "resolved-dns-rr.h"
/* A simple array of resource records. We keep track of the
* originating ifindex for each RR where that makes sense, so that we
* can qualify A and AAAA RRs referring to a local link with the
- * right ifindex. */
+ * right ifindex.
+ *
+ * Note that we usually encode the the empty DnsAnswer object as a simple NULL. */
+
+typedef enum DnsAnswerFlags {
+ DNS_ANSWER_AUTHENTICATED = 1, /* Item has been authenticated */
+ DNS_ANSWER_CACHEABLE = 2, /* Item is subject to caching */
+ DNS_ANSWER_SHARED_OWNER = 4, /* For mDNS: RRset may be owner by multiple peers */
+} DnsAnswerFlags;
struct DnsAnswerItem {
DnsResourceRecord *rr;
int ifindex;
+ DnsAnswerFlags flags;
};
struct DnsAnswer {
@@ -46,15 +56,90 @@ DnsAnswer *dns_answer_new(unsigned n);
DnsAnswer *dns_answer_ref(DnsAnswer *a);
DnsAnswer *dns_answer_unref(DnsAnswer *a);
-int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex);
+int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags);
+int dns_answer_add_extend(DnsAnswer **a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags);
int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl);
-int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key);
-int dns_answer_match_soa(DnsResourceKey *key, DnsResourceKey *soa);
-int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **ret);
-DnsAnswer *dns_answer_merge(DnsAnswer *a, DnsAnswer *b);
+int dns_answer_match_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *combined_flags);
+int dns_answer_contains_rr(DnsAnswer *a, DnsResourceRecord *rr, DnsAnswerFlags *combined_flags);
+int dns_answer_contains_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *combined_flags);
+int dns_answer_contains_nsec_or_nsec3(DnsAnswer *a);
+int dns_answer_contains_zone_nsec3(DnsAnswer *answer, const char *zone);
+
+int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags);
+int dns_answer_find_cname_or_dname(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags);
+
+int dns_answer_merge(DnsAnswer *a, DnsAnswer *b, DnsAnswer **ret);
+int dns_answer_extend(DnsAnswer **a, DnsAnswer *b);
+
void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local);
int dns_answer_reserve(DnsAnswer **a, unsigned n_free);
+int dns_answer_reserve_or_clone(DnsAnswer **a, unsigned n_free);
+
+int dns_answer_remove_by_key(DnsAnswer **a, const DnsResourceKey *key);
+int dns_answer_remove_by_rr(DnsAnswer **a, DnsResourceRecord *rr);
+
+int dns_answer_copy_by_key(DnsAnswer **a, DnsAnswer *source, const DnsResourceKey *key, DnsAnswerFlags or_flags);
+int dns_answer_move_by_key(DnsAnswer **to, DnsAnswer **from, const DnsResourceKey *key, DnsAnswerFlags or_flags);
+
+bool dns_answer_has_dname_for_cname(DnsAnswer *a, DnsResourceRecord *cname);
+
+static inline unsigned dns_answer_size(DnsAnswer *a) {
+ return a ? a->n_rrs : 0;
+}
+
+void dns_answer_dump(DnsAnswer *answer, FILE *f);
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsAnswer*, dns_answer_unref);
+
+#define _DNS_ANSWER_FOREACH(q, kk, a) \
+ for (unsigned UNIQ_T(i, q) = ({ \
+ (kk) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].rr : NULL; \
+ 0; \
+ }); \
+ (a) && (UNIQ_T(i, q) < (a)->n_rrs); \
+ UNIQ_T(i, q)++, (kk) = (UNIQ_T(i, q) < (a)->n_rrs ? (a)->items[UNIQ_T(i, q)].rr : NULL))
+
+#define DNS_ANSWER_FOREACH(kk, a) _DNS_ANSWER_FOREACH(UNIQ, kk, a)
+
+#define _DNS_ANSWER_FOREACH_IFINDEX(q, kk, ifi, a) \
+ for (unsigned UNIQ_T(i, q) = ({ \
+ (kk) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].rr : NULL; \
+ (ifi) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].ifindex : 0; \
+ 0; \
+ }); \
+ (a) && (UNIQ_T(i, q) < (a)->n_rrs); \
+ UNIQ_T(i, q)++, \
+ (kk) = ((UNIQ_T(i, q) < (a)->n_rrs) ? (a)->items[UNIQ_T(i, q)].rr : NULL), \
+ (ifi) = ((UNIQ_T(i, q) < (a)->n_rrs) ? (a)->items[UNIQ_T(i, q)].ifindex : 0))
+
+#define DNS_ANSWER_FOREACH_IFINDEX(kk, ifindex, a) _DNS_ANSWER_FOREACH_IFINDEX(UNIQ, kk, ifindex, a)
+
+#define _DNS_ANSWER_FOREACH_FLAGS(q, kk, fl, a) \
+ for (unsigned UNIQ_T(i, q) = ({ \
+ (kk) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].rr : NULL; \
+ (fl) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].flags : 0; \
+ 0; \
+ }); \
+ (a) && (UNIQ_T(i, q) < (a)->n_rrs); \
+ UNIQ_T(i, q)++, \
+ (kk) = ((UNIQ_T(i, q) < (a)->n_rrs) ? (a)->items[UNIQ_T(i, q)].rr : NULL), \
+ (fl) = ((UNIQ_T(i, q) < (a)->n_rrs) ? (a)->items[UNIQ_T(i, q)].flags : 0))
+
+#define DNS_ANSWER_FOREACH_FLAGS(kk, flags, a) _DNS_ANSWER_FOREACH_FLAGS(UNIQ, kk, flags, a)
+
+#define _DNS_ANSWER_FOREACH_FULL(q, kk, ifi, fl, a) \
+ for (unsigned UNIQ_T(i, q) = ({ \
+ (kk) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].rr : NULL; \
+ (ifi) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].ifindex : 0; \
+ (fl) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].flags : 0; \
+ 0; \
+ }); \
+ (a) && (UNIQ_T(i, q) < (a)->n_rrs); \
+ UNIQ_T(i, q)++, \
+ (kk) = ((UNIQ_T(i, q) < (a)->n_rrs) ? (a)->items[UNIQ_T(i, q)].rr : NULL), \
+ (ifi) = ((UNIQ_T(i, q) < (a)->n_rrs) ? (a)->items[UNIQ_T(i, q)].ifindex : 0), \
+ (fl) = ((UNIQ_T(i, q) < (a)->n_rrs) ? (a)->items[UNIQ_T(i, q)].flags : 0))
+
+#define DNS_ANSWER_FOREACH_FULL(kk, ifindex, flags, a) _DNS_ANSWER_FOREACH_FULL(UNIQ, kk, ifindex, flags, a)
diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c
index ab13636bc1..fdb34d11df 100644
--- a/src/resolve/resolved-dns-cache.c
+++ b/src/resolve/resolved-dns-cache.c
@@ -19,14 +19,19 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "alloc-util.h"
+#include "dns-domain.h"
+#include "resolved-dns-answer.h"
#include "resolved-dns-cache.h"
#include "resolved-dns-packet.h"
+#include "string-util.h"
-/* Never cache more than 1K entries */
-#define CACHE_MAX 1024
+/* Never cache more than 4K entries. RFC 1536, Section 5 suggests to
+ * leave DNS caches unbounded, but that's crazy. */
+#define CACHE_MAX 4096
-/* We never keep any item longer than 10min in our cache */
-#define CACHE_TTL_MAX_USEC (10 * USEC_PER_MINUTE)
+/* We never keep any item longer than 2h in our cache */
+#define CACHE_TTL_MAX_USEC (2 * USEC_PER_HOUR)
typedef enum DnsCacheItemType DnsCacheItemType;
typedef struct DnsCacheItem DnsCacheItem;
@@ -38,13 +43,18 @@ enum DnsCacheItemType {
};
struct DnsCacheItem {
+ DnsCacheItemType type;
DnsResourceKey *key;
DnsResourceRecord *rr;
+
usec_t until;
- DnsCacheItemType type;
- unsigned prioq_idx;
+ bool authenticated:1;
+ bool shared_owner:1;
+
int owner_family;
union in_addr_union owner_address;
+
+ unsigned prioq_idx;
LIST_FIELDS(DnsCacheItem, by_key);
};
@@ -59,7 +69,7 @@ static void dns_cache_item_free(DnsCacheItem *i) {
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsCacheItem*, dns_cache_item_free);
-static void dns_cache_item_remove_and_free(DnsCache *c, DnsCacheItem *i) {
+static void dns_cache_item_unlink_and_free(DnsCache *c, DnsCacheItem *i) {
DnsCacheItem *first;
assert(c);
@@ -80,34 +90,55 @@ static void dns_cache_item_remove_and_free(DnsCache *c, DnsCacheItem *i) {
dns_cache_item_free(i);
}
-void dns_cache_flush(DnsCache *c) {
- DnsCacheItem *i;
+static bool dns_cache_remove_by_rr(DnsCache *c, DnsResourceRecord *rr) {
+ DnsCacheItem *first, *i;
+ int r;
+
+ first = hashmap_get(c->by_key, rr->key);
+ LIST_FOREACH(by_key, i, first) {
+ r = dns_resource_record_equal(i->rr, rr);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ dns_cache_item_unlink_and_free(c, i);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool dns_cache_remove_by_key(DnsCache *c, DnsResourceKey *key) {
+ DnsCacheItem *first, *i, *n;
assert(c);
+ assert(key);
- while ((i = hashmap_first(c->by_key)))
- dns_cache_item_remove_and_free(c, i);
+ first = hashmap_remove(c->by_key, key);
+ if (!first)
+ return false;
- assert(hashmap_size(c->by_key) == 0);
- assert(prioq_size(c->by_expiry) == 0);
+ LIST_FOREACH_SAFE(by_key, i, n, first) {
+ prioq_remove(c->by_expiry, i, &i->prioq_idx);
+ dns_cache_item_free(i);
+ }
- c->by_key = hashmap_free(c->by_key);
- c->by_expiry = prioq_free(c->by_expiry);
+ return true;
}
-static bool dns_cache_remove(DnsCache *c, DnsResourceKey *key) {
- DnsCacheItem *i;
- bool exist = false;
+void dns_cache_flush(DnsCache *c) {
+ DnsResourceKey *key;
assert(c);
- assert(key);
- while ((i = hashmap_get(c->by_key, key))) {
- dns_cache_item_remove_and_free(c, i);
- exist = true;
- }
+ while ((key = hashmap_first_key(c->by_key)))
+ dns_cache_remove_by_key(c, key);
+
+ assert(hashmap_size(c->by_key) == 0);
+ assert(prioq_size(c->by_expiry) == 0);
- return exist;
+ c->by_key = hashmap_free(c->by_key);
+ c->by_expiry = prioq_free(c->by_expiry);
}
static void dns_cache_make_space(DnsCache *c, unsigned add) {
@@ -137,7 +168,7 @@ static void dns_cache_make_space(DnsCache *c, unsigned add) {
/* Take an extra reference to the key so that it
* doesn't go away in the middle of the remove call */
key = dns_resource_key_ref(i->key);
- dns_cache_remove(c, key);
+ dns_cache_remove_by_key(c, key);
}
}
@@ -149,7 +180,6 @@ void dns_cache_prune(DnsCache *c) {
/* Remove all entries that are past their TTL */
for (;;) {
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
DnsCacheItem *i;
i = prioq_peek(c->by_expiry);
@@ -162,10 +192,19 @@ void dns_cache_prune(DnsCache *c) {
if (i->until > t)
break;
- /* Take an extra reference to the key so that it
- * doesn't go away in the middle of the remove call */
- key = dns_resource_key_ref(i->key);
- dns_cache_remove(c, key);
+ /* Depending whether this is an mDNS shared entry
+ * either remove only this one RR or the whole
+ * RRset */
+ if (i->shared_owner)
+ dns_cache_item_unlink_and_free(c, i);
+ else {
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
+
+ /* Take an extra reference to the key so that it
+ * doesn't go away in the middle of the remove call */
+ key = dns_resource_key_ref(i->key);
+ dns_cache_remove_by_key(c, key);
+ }
}
}
@@ -208,6 +247,19 @@ static int dns_cache_link_item(DnsCache *c, DnsCacheItem *i) {
first = hashmap_get(c->by_key, i->key);
if (first) {
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *k = NULL;
+
+ /* Keep a reference to the original key, while we manipulate the list. */
+ k = dns_resource_key_ref(first->key);
+
+ /* Now, try to reduce the number of keys we keep */
+ dns_resource_key_reduce(&first->key, &i->key);
+
+ if (first->rr)
+ dns_resource_key_reduce(&first->rr->key, &i->key);
+ if (i->rr)
+ dns_resource_key_reduce(&i->rr->key, &i->key);
+
LIST_PREPEND(by_key, first, i);
assert_se(hashmap_replace(c->by_key, first->key, first) >= 0);
} else {
@@ -234,10 +286,56 @@ static DnsCacheItem* dns_cache_get(DnsCache *c, DnsResourceRecord *rr) {
return NULL;
}
-static void dns_cache_item_update_positive(DnsCache *c, DnsCacheItem *i, DnsResourceRecord *rr, usec_t timestamp) {
+static usec_t calculate_until(DnsResourceRecord *rr, uint32_t nsec_ttl, usec_t timestamp, bool use_soa_minimum) {
+ uint32_t ttl;
+ usec_t u;
+
+ assert(rr);
+
+ ttl = MIN(rr->ttl, nsec_ttl);
+ if (rr->key->type == DNS_TYPE_SOA && use_soa_minimum) {
+ /* If this is a SOA RR, and it is requested, clamp to
+ * the SOA's minimum field. This is used when we do
+ * negative caching, to determine the TTL for the
+ * negative caching entry. See RFC 2308, Section
+ * 5. */
+
+ if (ttl > rr->soa.minimum)
+ ttl = rr->soa.minimum;
+ }
+
+ u = ttl * USEC_PER_SEC;
+ if (u > CACHE_TTL_MAX_USEC)
+ u = CACHE_TTL_MAX_USEC;
+
+ if (rr->expiry != USEC_INFINITY) {
+ usec_t left;
+
+ /* Make use of the DNSSEC RRSIG expiry info, if we
+ * have it */
+
+ left = LESS_BY(rr->expiry, now(CLOCK_REALTIME));
+ if (u > left)
+ u = left;
+ }
+
+ return timestamp + u;
+}
+
+static void dns_cache_item_update_positive(
+ DnsCache *c,
+ DnsCacheItem *i,
+ DnsResourceRecord *rr,
+ bool authenticated,
+ bool shared_owner,
+ usec_t timestamp,
+ int owner_family,
+ const union in_addr_union *owner_address) {
+
assert(c);
assert(i);
assert(rr);
+ assert(owner_address);
i->type = DNS_CACHE_POSITIVE;
@@ -254,7 +352,12 @@ static void dns_cache_item_update_positive(DnsCache *c, DnsCacheItem *i, DnsReso
dns_resource_key_unref(i->key);
i->key = dns_resource_key_ref(rr->key);
- i->until = timestamp + MIN(rr->ttl * USEC_PER_SEC, CACHE_TTL_MAX_USEC);
+ i->until = calculate_until(rr, (uint32_t) -1, timestamp, false);
+ i->authenticated = authenticated;
+ i->shared_owner = shared_owner;
+
+ i->owner_family = owner_family;
+ i->owner_address = *owner_address;
prioq_reshuffle(c->by_expiry, i, &i->prioq_idx);
}
@@ -262,6 +365,8 @@ static void dns_cache_item_update_positive(DnsCache *c, DnsCacheItem *i, DnsReso
static int dns_cache_put_positive(
DnsCache *c,
DnsResourceRecord *rr,
+ bool authenticated,
+ bool shared_owner,
usec_t timestamp,
int owner_family,
const union in_addr_union *owner_address) {
@@ -269,35 +374,48 @@ static int dns_cache_put_positive(
_cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL;
_cleanup_free_ char *key_str = NULL;
DnsCacheItem *existing;
- int r;
+ int r, k;
assert(c);
assert(rr);
assert(owner_address);
- /* New TTL is 0? Delete the entry... */
+ /* Never cache pseudo RRs */
+ if (dns_class_is_pseudo(rr->key->class))
+ return 0;
+ if (dns_type_is_pseudo(rr->key->type))
+ return 0;
+
+ /* New TTL is 0? Delete this specific entry... */
if (rr->ttl <= 0) {
- r = dns_resource_key_to_string(rr->key, &key_str);
- if (r < 0)
- return r;
+ k = dns_cache_remove_by_rr(c, rr);
- if (dns_cache_remove(c, rr->key))
- log_debug("Removed zero TTL entry from cache: %s", key_str);
- else
- log_debug("Not caching zero TTL cache entry: %s", key_str);
+ if (log_get_max_level() >= LOG_DEBUG) {
+ r = dns_resource_key_to_string(rr->key, &key_str);
+ if (r < 0)
+ return r;
- return 0;
- }
+ if (k > 0)
+ log_debug("Removed zero TTL entry from cache: %s", key_str);
+ else
+ log_debug("Not caching zero TTL cache entry: %s", key_str);
+ }
- if (rr->key->class == DNS_CLASS_ANY)
- return 0;
- if (rr->key->type == DNS_TYPE_ANY)
return 0;
+ }
- /* Entry exists already? Update TTL and timestamp */
+ /* Entry exists already? Update TTL, timestamp and owner*/
existing = dns_cache_get(c, rr);
if (existing) {
- dns_cache_item_update_positive(c, existing, rr, timestamp);
+ dns_cache_item_update_positive(
+ c,
+ existing,
+ rr,
+ authenticated,
+ shared_owner,
+ timestamp,
+ owner_family,
+ owner_address);
return 0;
}
@@ -315,20 +433,24 @@ static int dns_cache_put_positive(
i->type = DNS_CACHE_POSITIVE;
i->key = dns_resource_key_ref(rr->key);
i->rr = dns_resource_record_ref(rr);
- i->until = timestamp + MIN(i->rr->ttl * USEC_PER_SEC, CACHE_TTL_MAX_USEC);
- i->prioq_idx = PRIOQ_IDX_NULL;
+ i->until = calculate_until(rr, (uint32_t) -1, timestamp, false);
+ i->authenticated = authenticated;
+ i->shared_owner = shared_owner;
i->owner_family = owner_family;
i->owner_address = *owner_address;
+ i->prioq_idx = PRIOQ_IDX_NULL;
r = dns_cache_link_item(c, i);
if (r < 0)
return r;
- r = dns_resource_key_to_string(i->key, &key_str);
- if (r < 0)
- return r;
+ if (log_get_max_level() >= LOG_DEBUG) {
+ r = dns_resource_key_to_string(i->key, &key_str);
+ if (r < 0)
+ return r;
- log_debug("Added cache entry for %s", key_str);
+ log_debug("Added positive cache entry for %s", key_str);
+ }
i = NULL;
return 0;
@@ -338,8 +460,10 @@ static int dns_cache_put_negative(
DnsCache *c,
DnsResourceKey *key,
int rcode,
+ bool authenticated,
+ uint32_t nsec_ttl,
usec_t timestamp,
- uint32_t soa_ttl,
+ DnsResourceRecord *soa,
int owner_family,
const union in_addr_union *owner_address) {
@@ -349,20 +473,25 @@ static int dns_cache_put_negative(
assert(c);
assert(key);
+ assert(soa);
assert(owner_address);
- dns_cache_remove(c, key);
-
- if (key->class == DNS_CLASS_ANY)
+ /* Never cache pseudo RR keys. DNS_TYPE_ANY is particularly
+ * important to filter out as we use this as a pseudo-type for
+ * NXDOMAIN entries */
+ if (dns_class_is_pseudo(key->class))
return 0;
- if (key->type == DNS_TYPE_ANY)
+ if (dns_type_is_pseudo(key->type))
return 0;
- if (soa_ttl <= 0) {
- r = dns_resource_key_to_string(key, &key_str);
- if (r < 0)
- return r;
- log_debug("Not caching negative entry with zero SOA TTL: %s", key_str);
+ if (nsec_ttl <= 0 || soa->soa.minimum <= 0 || soa->ttl <= 0) {
+ if (log_get_max_level() >= LOG_DEBUG) {
+ r = dns_resource_key_to_string(key, &key_str);
+ if (r < 0)
+ return r;
+
+ log_debug("Not caching negative entry with zero SOA/NSEC/NSEC3 TTL: %s", key_str);
+ }
return 0;
}
@@ -381,63 +510,117 @@ static int dns_cache_put_negative(
return -ENOMEM;
i->type = rcode == DNS_RCODE_SUCCESS ? DNS_CACHE_NODATA : DNS_CACHE_NXDOMAIN;
- i->key = dns_resource_key_ref(key);
- i->until = timestamp + MIN(soa_ttl * USEC_PER_SEC, CACHE_TTL_MAX_USEC);
- i->prioq_idx = PRIOQ_IDX_NULL;
+ i->until = calculate_until(soa, nsec_ttl, timestamp, true);
+ i->authenticated = authenticated;
i->owner_family = owner_family;
i->owner_address = *owner_address;
+ i->prioq_idx = PRIOQ_IDX_NULL;
+
+ if (i->type == DNS_CACHE_NXDOMAIN) {
+ /* NXDOMAIN entries should apply equally to all types, so we use ANY as
+ * a pseudo type for this purpose here. */
+ i->key = dns_resource_key_new(key->class, DNS_TYPE_ANY, DNS_RESOURCE_KEY_NAME(key));
+ if (!i->key)
+ return -ENOMEM;
+
+ /* Make sure to remove any previous entry for this
+ * specific ANY key. (For non-ANY keys the cache data
+ * is already cleared by the caller.) Note that we
+ * don't bother removing positive or NODATA cache
+ * items in this case, because it would either be slow
+ * or require explicit indexing by name */
+ dns_cache_remove_by_key(c, key);
+ } else
+ i->key = dns_resource_key_ref(key);
r = dns_cache_link_item(c, i);
if (r < 0)
return r;
- r = dns_resource_key_to_string(i->key, &key_str);
- if (r < 0)
- return r;
+ if (log_get_max_level() >= LOG_DEBUG) {
+ r = dns_resource_key_to_string(i->key, &key_str);
+ if (r < 0)
+ return r;
- log_debug("Added %s cache entry for %s", i->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", key_str);
+ log_debug("Added %s cache entry for %s", i->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", key_str);
+ }
i = NULL;
return 0;
}
+static void dns_cache_remove_previous(
+ DnsCache *c,
+ DnsResourceKey *key,
+ DnsAnswer *answer) {
+
+ DnsResourceRecord *rr;
+ DnsAnswerFlags flags;
+
+ assert(c);
+
+ /* First, if we were passed a key (i.e. on LLMNR/DNS, but
+ * not on mDNS), delete all matching old RRs, so that we only
+ * keep complete by_key in place. */
+ if (key)
+ dns_cache_remove_by_key(c, key);
+
+ /* Second, flush all entries matching the answer, unless this
+ * is an RR that is explicitly marked to be "shared" between
+ * peers (i.e. mDNS RRs without the flush-cache bit set). */
+ DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
+ if ((flags & DNS_ANSWER_CACHEABLE) == 0)
+ continue;
+
+ if (flags & DNS_ANSWER_SHARED_OWNER)
+ continue;
+
+ dns_cache_remove_by_key(c, rr->key);
+ }
+}
+
int dns_cache_put(
DnsCache *c,
DnsResourceKey *key,
int rcode,
DnsAnswer *answer,
- unsigned max_rrs,
+ bool authenticated,
+ uint32_t nsec_ttl,
usec_t timestamp,
int owner_family,
const union in_addr_union *owner_address) {
- DnsResourceRecord *soa = NULL;
- unsigned cache_keys, i;
+ DnsResourceRecord *soa = NULL, *rr;
+ DnsAnswerFlags flags;
+ unsigned cache_keys;
int r;
assert(c);
+ assert(owner_address);
- if (key) {
- /* First, if we were passed a key, delete all matching old RRs,
- * so that we only keep complete by_key in place. */
- dns_cache_remove(c, key);
- }
+ dns_cache_remove_previous(c, key, answer);
- if (!answer)
- return 0;
+ if (dns_answer_size(answer) <= 0) {
+ if (log_get_max_level() >= LOG_DEBUG) {
+ _cleanup_free_ char *key_str = NULL;
+
+ r = dns_resource_key_to_string(key, &key_str);
+ if (r < 0)
+ return r;
- for (i = 0; i < answer->n_rrs; i++)
- dns_cache_remove(c, answer->items[i].rr->key);
+ log_debug("Not caching negative entry without a SOA record: %s", key_str);
+ }
+
+ return 0;
+ }
/* We only care for positive replies and NXDOMAINs, on all
* other replies we will simply flush the respective entries,
* and that's it */
-
if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN))
return 0;
- cache_keys = answer->n_rrs;
-
+ cache_keys = dns_answer_size(answer);
if (key)
cache_keys ++;
@@ -448,58 +631,63 @@ int dns_cache_put(
timestamp = now(clock_boottime_or_monotonic());
/* Second, add in positive entries for all contained RRs */
- for (i = 0; i < MIN(max_rrs, answer->n_rrs); i++) {
- r = dns_cache_put_positive(c, answer->items[i].rr, timestamp, owner_family, owner_address);
+ DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
+ if ((flags & DNS_ANSWER_CACHEABLE) == 0)
+ continue;
+
+ r = dns_cache_put_positive(
+ c,
+ rr,
+ flags & DNS_ANSWER_AUTHENTICATED,
+ flags & DNS_ANSWER_SHARED_OWNER,
+ timestamp,
+ owner_family, owner_address);
if (r < 0)
goto fail;
}
- if (!key)
+ if (!key) /* mDNS doesn't know negative caching, really */
return 0;
/* Third, add in negative entries if the key has no RR */
- r = dns_answer_contains(answer, key);
+ r = dns_answer_match_key(answer, key, NULL);
if (r < 0)
goto fail;
if (r > 0)
return 0;
- /* See https://tools.ietf.org/html/rfc2308, which
- * say that a matching SOA record in the packet
- * is used to to enable negative caching. */
+ /* But not if it has a matching CNAME/DNAME (the negative
+ * caching will be done on the canonical name, not on the
+ * alias) */
+ r = dns_answer_find_cname_or_dname(answer, key, NULL, NULL);
+ if (r < 0)
+ goto fail;
+ if (r > 0)
+ return 0;
- r = dns_answer_find_soa(answer, key, &soa);
+ /* See https://tools.ietf.org/html/rfc2308, which say that a
+ * matching SOA record in the packet is used to to enable
+ * negative caching. */
+ r = dns_answer_find_soa(answer, key, &soa, &flags);
if (r < 0)
goto fail;
if (r == 0)
return 0;
- /* Also, if the requested key is an alias, the negative response should
- be cached for each name in the redirect chain. Any CNAME record in
- the response is from the redirection chain, though only the final one
- is guaranteed to be included. This means that we cannot verify the
- chain and that we need to cache them all as it may be incomplete. */
- for (i = 0; i < answer->n_rrs; i++) {
- DnsResourceRecord *answer_rr = answer->items[i].rr;
-
- if (answer_rr->key->type == DNS_TYPE_CNAME) {
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *canonical_key = NULL;
-
- canonical_key = dns_resource_key_new_redirect(key, answer_rr);
- if (!canonical_key)
- goto fail;
-
- /* Let's not add negative cache entries for records outside the current zone. */
- if (!dns_answer_match_soa(canonical_key, soa->key))
- continue;
-
- r = dns_cache_put_negative(c, canonical_key, rcode, timestamp, MIN(soa->soa.minimum, soa->ttl), owner_family, owner_address);
- if (r < 0)
- goto fail;
- }
- }
+ /* Refuse using the SOA data if it is unsigned, but the key is
+ * signed */
+ if (authenticated && (flags & DNS_ANSWER_AUTHENTICATED) == 0)
+ return 0;
- r = dns_cache_put_negative(c, key, rcode, timestamp, MIN(soa->soa.minimum, soa->ttl), owner_family, owner_address);
+ r = dns_cache_put_negative(
+ c,
+ key,
+ rcode,
+ authenticated,
+ nsec_ttl,
+ timestamp,
+ soa,
+ owner_family, owner_address);
if (r < 0)
goto fail;
@@ -510,75 +698,122 @@ fail:
* added, just in case */
if (key)
- dns_cache_remove(c, key);
+ dns_cache_remove_by_key(c, key);
- for (i = 0; i < answer->n_rrs; i++)
- dns_cache_remove(c, answer->items[i].rr->key);
+ DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
+ if ((flags & DNS_ANSWER_CACHEABLE) == 0)
+ continue;
+
+ dns_cache_remove_by_key(c, rr->key);
+ }
return r;
}
-static DnsCacheItem *dns_cache_get_by_key_follow_cname(DnsCache *c, DnsResourceKey *k) {
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *cname_key = NULL;
- DnsCacheItem *i, *j;
+static DnsCacheItem *dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache *c, DnsResourceKey *k) {
+ DnsCacheItem *i;
+ const char *n;
+ int r;
assert(c);
assert(k);
+ /* If we hit some OOM error, or suchlike, we don't care too
+ * much, after all this is just a cache */
+
i = hashmap_get(c->by_key, k);
- if (i || k->type == DNS_TYPE_CNAME)
+ if (i)
return i;
- /* check if we have a CNAME record instead */
- cname_key = dns_resource_key_new_cname(k);
- if (!cname_key)
- return NULL;
+ n = DNS_RESOURCE_KEY_NAME(k);
+
+ /* Check if we have an NXDOMAIN cache item for the name, notice that we use
+ * the pseudo-type ANY for NXDOMAIN cache items. */
+ i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_ANY, n));
+ if (i && i->type == DNS_CACHE_NXDOMAIN)
+ return i;
+
+ if (dns_type_may_redirect(k->type)) {
+ /* Check if we have a CNAME record instead */
+ i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_CNAME, n));
+ if (i)
+ return i;
- j = hashmap_get(c->by_key, cname_key);
- if (j)
- return j;
+ /* OK, let's look for cached DNAME records. */
+ for (;;) {
+ if (isempty(n))
+ return NULL;
+
+ i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_DNAME, n));
+ if (i)
+ return i;
+
+ /* Jump one label ahead */
+ r = dns_name_parent(&n);
+ if (r <= 0)
+ return NULL;
+ }
+ }
+
+ if (k->type != DNS_TYPE_NSEC) {
+ /* Check if we have an NSEC record instead for the name. */
+ i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_NSEC, n));
+ if (i)
+ return i;
+ }
- return i;
+ return NULL;
}
-int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **ret) {
+int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **ret, bool *authenticated) {
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
unsigned n = 0;
int r;
bool nxdomain = false;
_cleanup_free_ char *key_str = NULL;
- DnsCacheItem *j, *first;
+ DnsCacheItem *j, *first, *nsec = NULL;
+ bool have_authenticated = false, have_non_authenticated = false;
assert(c);
assert(key);
assert(rcode);
assert(ret);
+ assert(authenticated);
if (key->type == DNS_TYPE_ANY ||
key->class == DNS_CLASS_ANY) {
- /* If we have ANY lookups we simply refresh */
+ /* If we have ANY lookups we don't use the cache, so
+ * that the caller refreshes via the network. */
- r = dns_resource_key_to_string(key, &key_str);
- if (r < 0)
- return r;
+ if (log_get_max_level() >= LOG_DEBUG) {
+ r = dns_resource_key_to_string(key, &key_str);
+ if (r < 0)
+ return r;
+
+ log_debug("Ignoring cache for ANY lookup: %s", key_str);
+ }
- log_debug("Ignoring cache for ANY lookup: %s", key_str);
+ c->n_miss++;
*ret = NULL;
*rcode = DNS_RCODE_SUCCESS;
return 0;
}
- first = dns_cache_get_by_key_follow_cname(c, key);
+ first = dns_cache_get_by_key_follow_cname_dname_nsec(c, key);
if (!first) {
/* If one question cannot be answered we need to refresh */
- r = dns_resource_key_to_string(key, &key_str);
- if (r < 0)
- return r;
+ if (log_get_max_level() >= LOG_DEBUG) {
+ r = dns_resource_key_to_string(key, &key_str);
+ if (r < 0)
+ return r;
- log_debug("Cache miss for %s", key_str);
+ log_debug("Cache miss for %s", key_str);
+ }
+
+ c->n_miss++;
*ret = NULL;
*rcode = DNS_RCODE_SUCCESS;
@@ -586,24 +821,65 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r
}
LIST_FOREACH(by_key, j, first) {
- if (j->rr)
+ if (j->rr) {
+ if (j->rr->key->type == DNS_TYPE_NSEC)
+ nsec = j;
+
n++;
- else if (j->type == DNS_CACHE_NXDOMAIN)
+ } else if (j->type == DNS_CACHE_NXDOMAIN)
nxdomain = true;
+
+ if (j->authenticated)
+ have_authenticated = true;
+ else
+ have_non_authenticated = true;
}
- r = dns_resource_key_to_string(key, &key_str);
- if (r < 0)
- return r;
+ if (nsec && key->type != DNS_TYPE_NSEC) {
+ if (log_get_max_level() >= LOG_DEBUG) {
+ r = dns_resource_key_to_string(key, &key_str);
+ if (r < 0)
+ return r;
+
+ log_debug("NSEC NODATA cache hit for %s", key_str);
+ }
+
+ /* We only found an NSEC record that matches our name.
+ * If it says the type doesn't exist report
+ * NODATA. Otherwise report a cache miss. */
- log_debug("%s cache hit for %s",
- nxdomain ? "NXDOMAIN" :
- n > 0 ? "Positive" : "NODATA",
- key_str);
+ *ret = NULL;
+ *rcode = DNS_RCODE_SUCCESS;
+ *authenticated = nsec->authenticated;
+
+ if (!bitmap_isset(nsec->rr->nsec.types, key->type) &&
+ !bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_CNAME) &&
+ !bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_DNAME)) {
+ c->n_hit++;
+ return 1;
+ }
+
+ c->n_miss++;
+ return 0;
+ }
+
+ if (log_get_max_level() >= LOG_DEBUG) {
+ r = dns_resource_key_to_string(key, &key_str);
+ if (r < 0)
+ return r;
+
+ log_debug("%s cache hit for %s",
+ n > 0 ? "Positive" :
+ nxdomain ? "NXDOMAIN" : "NODATA",
+ key_str);
+ }
if (n <= 0) {
+ c->n_hit++;
+
*ret = NULL;
*rcode = nxdomain ? DNS_RCODE_NXDOMAIN : DNS_RCODE_SUCCESS;
+ *authenticated = have_authenticated && !have_non_authenticated;
return 1;
}
@@ -615,13 +891,16 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r
if (!j->rr)
continue;
- r = dns_answer_add(answer, j->rr, 0);
+ r = dns_answer_add(answer, j->rr, 0, j->authenticated ? DNS_ANSWER_AUTHENTICATED : 0);
if (r < 0)
return r;
}
+ c->n_hit++;
+
*ret = answer;
*rcode = DNS_RCODE_SUCCESS;
+ *authenticated = have_authenticated && !have_non_authenticated;
answer = NULL;
return n;
@@ -663,6 +942,55 @@ int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_
return 1;
}
+int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) {
+ unsigned ancount = 0;
+ Iterator iterator;
+ DnsCacheItem *i;
+ int r;
+
+ assert(cache);
+ assert(p);
+
+ HASHMAP_FOREACH(i, cache->by_key, iterator) {
+ DnsCacheItem *j;
+
+ LIST_FOREACH(by_key, j, i) {
+ if (!j->rr)
+ continue;
+
+ if (!j->shared_owner)
+ continue;
+
+ r = dns_packet_append_rr(p, j->rr, NULL, NULL);
+ if (r == -EMSGSIZE && p->protocol == DNS_PROTOCOL_MDNS) {
+ /* For mDNS, if we're unable to stuff all known answers into the given packet,
+ * allocate a new one, push the RR into that one and link it to the current one.
+ */
+
+ DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
+ ancount = 0;
+
+ r = dns_packet_new_query(&p->more, p->protocol, 0, true);
+ if (r < 0)
+ return r;
+
+ /* continue with new packet */
+ p = p->more;
+ r = dns_packet_append_rr(p, j->rr, NULL, NULL);
+ }
+
+ if (r < 0)
+ return r;
+
+ ancount ++;
+ }
+ }
+
+ DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
+
+ return 0;
+}
+
void dns_cache_dump(DnsCache *cache, FILE *f) {
Iterator iterator;
DnsCacheItem *i;
@@ -678,13 +1006,13 @@ void dns_cache_dump(DnsCache *cache, FILE *f) {
DnsCacheItem *j;
LIST_FOREACH(by_key, j, i) {
- _cleanup_free_ char *t = NULL;
fputc('\t', f);
if (j->rr) {
- r = dns_resource_record_to_string(j->rr, &t);
- if (r < 0) {
+ const char *t;
+ t = dns_resource_record_to_string(j->rr);
+ if (!t) {
log_oom();
continue;
}
@@ -692,13 +1020,14 @@ void dns_cache_dump(DnsCache *cache, FILE *f) {
fputs(t, f);
fputc('\n', f);
} else {
- r = dns_resource_key_to_string(j->key, &t);
+ _cleanup_free_ char *z = NULL;
+ r = dns_resource_key_to_string(j->key, &z);
if (r < 0) {
log_oom();
continue;
}
- fputs(t, f);
+ fputs(z, f);
fputs(" -- ", f);
fputs(j->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", f);
fputc('\n', f);
@@ -713,3 +1042,10 @@ bool dns_cache_is_empty(DnsCache *cache) {
return hashmap_isempty(cache->by_key);
}
+
+unsigned dns_cache_size(DnsCache *cache) {
+ if (!cache)
+ return 0;
+
+ return hashmap_size(cache->by_key);
+}
diff --git a/src/resolve/resolved-dns-cache.h b/src/resolve/resolved-dns-cache.h
index 60cf6a4784..e61b285df4 100644
--- a/src/resolve/resolved-dns-cache.h
+++ b/src/resolve/resolved-dns-cache.h
@@ -21,28 +21,34 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-
#include "hashmap.h"
+#include "list.h"
#include "prioq.h"
#include "time-util.h"
-#include "list.h"
typedef struct DnsCache {
Hashmap *by_key;
Prioq *by_expiry;
+ unsigned n_hit;
+ unsigned n_miss;
} DnsCache;
-#include "resolved-dns-rr.h"
-#include "resolved-dns-question.h"
#include "resolved-dns-answer.h"
+#include "resolved-dns-packet.h"
+#include "resolved-dns-question.h"
+#include "resolved-dns-rr.h"
void dns_cache_flush(DnsCache *c);
void dns_cache_prune(DnsCache *c);
-int dns_cache_put(DnsCache *c, DnsResourceKey *key, int rcode, DnsAnswer *answer, unsigned max_rrs, usec_t timestamp, int owner_family, const union in_addr_union *owner_address);
-int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **answer);
+int dns_cache_put(DnsCache *c, DnsResourceKey *key, int rcode, DnsAnswer *answer, bool authenticated, uint32_t nsec_ttl, usec_t timestamp, int owner_family, const union in_addr_union *owner_address);
+int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **answer, bool *authenticated);
int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address);
void dns_cache_dump(DnsCache *cache, FILE *f);
bool dns_cache_is_empty(DnsCache *cache);
+
+unsigned dns_cache_size(DnsCache *cache);
+
+int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p);
diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c
new file mode 100644
index 0000000000..1f48f588ce
--- /dev/null
+++ b/src/resolve/resolved-dns-dnssec.c
@@ -0,0 +1,2131 @@
+/*-*- 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 <gcrypt.h>
+
+#include "alloc-util.h"
+#include "dns-domain.h"
+#include "hexdecoct.h"
+#include "resolved-dns-dnssec.h"
+#include "resolved-dns-packet.h"
+#include "string-table.h"
+
+/* Open question:
+ *
+ * How does the DNSSEC canonical form of a hostname with a label
+ * containing a dot look like, the way DNS-SD does it?
+ *
+ * TODO:
+ *
+ * - bus calls to override DNSEC setting per interface
+ * - log all DNSSEC downgrades
+ * - log all RRs that failed validation
+ * - enable by default
+ * - Allow clients to request DNSSEC even if DNSSEC is off
+ * - make sure when getting an NXDOMAIN response through CNAME, we still process the first CNAMEs in the packet
+ * */
+
+#define VERIFY_RRS_MAX 256
+#define MAX_KEY_SIZE (32*1024)
+
+/* Permit a maximum clock skew of 1h 10min. This should be enough to deal with DST confusion */
+#define SKEW_MAX (1*USEC_PER_HOUR + 10*USEC_PER_MINUTE)
+
+/* Maximum number of NSEC3 iterations we'll do. RFC5155 says 2500 shall be the maximum useful value */
+#define NSEC3_ITERATIONS_MAX 2500
+
+/*
+ * The DNSSEC Chain of trust:
+ *
+ * Normal RRs are protected via RRSIG RRs in combination with DNSKEY RRs, all in the same zone
+ * DNSKEY RRs are either protected like normal RRs, or via a DS from a zone "higher" up the tree
+ * DS RRs are protected like normal RRs
+ *
+ * Example chain:
+ * Normal RR → RRSIG/DNSKEY+ → DS → RRSIG/DNSKEY+ → DS → ... → DS → RRSIG/DNSKEY+ → DS
+ */
+
+static void initialize_libgcrypt(void) {
+ const char *p;
+
+ if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
+ return;
+
+ p = gcry_check_version("1.4.5");
+ assert(p);
+
+ gcry_control(GCRYCTL_DISABLE_SECMEM);
+ gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
+}
+
+uint16_t dnssec_keytag(DnsResourceRecord *dnskey, bool mask_revoke) {
+ const uint8_t *p;
+ uint32_t sum, f;
+ size_t i;
+
+ /* The algorithm from RFC 4034, Appendix B. */
+
+ assert(dnskey);
+ assert(dnskey->key->type == DNS_TYPE_DNSKEY);
+
+ f = (uint32_t) dnskey->dnskey.flags;
+
+ if (mask_revoke)
+ f &= ~DNSKEY_FLAG_REVOKE;
+
+ sum = f + ((((uint32_t) dnskey->dnskey.protocol) << 8) + (uint32_t) dnskey->dnskey.algorithm);
+
+ p = dnskey->dnskey.key;
+
+ for (i = 0; i < dnskey->dnskey.key_size; i++)
+ sum += (i & 1) == 0 ? (uint32_t) p[i] << 8 : (uint32_t) p[i];
+
+ sum += (sum >> 16) & UINT32_C(0xFFFF);
+
+ return sum & UINT32_C(0xFFFF);
+}
+
+static int rr_compare(const void *a, const void *b) {
+ DnsResourceRecord **x = (DnsResourceRecord**) a, **y = (DnsResourceRecord**) b;
+ size_t m;
+ int r;
+
+ /* Let's order the RRs according to RFC 4034, Section 6.3 */
+
+ assert(x);
+ assert(*x);
+ assert((*x)->wire_format);
+ assert(y);
+ assert(*y);
+ assert((*y)->wire_format);
+
+ m = MIN(DNS_RESOURCE_RECORD_RDATA_SIZE(*x), DNS_RESOURCE_RECORD_RDATA_SIZE(*y));
+
+ r = memcmp(DNS_RESOURCE_RECORD_RDATA(*x), DNS_RESOURCE_RECORD_RDATA(*y), m);
+ if (r != 0)
+ return r;
+
+ if (DNS_RESOURCE_RECORD_RDATA_SIZE(*x) < DNS_RESOURCE_RECORD_RDATA_SIZE(*y))
+ return -1;
+ else if (DNS_RESOURCE_RECORD_RDATA_SIZE(*x) > DNS_RESOURCE_RECORD_RDATA_SIZE(*y))
+ return 1;
+
+ return 0;
+}
+
+static int dnssec_rsa_verify_raw(
+ const char *hash_algorithm,
+ const void *signature, size_t signature_size,
+ const void *data, size_t data_size,
+ const void *exponent, size_t exponent_size,
+ const void *modulus, size_t modulus_size) {
+
+ gcry_sexp_t public_key_sexp = NULL, data_sexp = NULL, signature_sexp = NULL;
+ gcry_mpi_t n = NULL, e = NULL, s = NULL;
+ gcry_error_t ge;
+ int r;
+
+ assert(hash_algorithm);
+
+ ge = gcry_mpi_scan(&s, GCRYMPI_FMT_USG, signature, signature_size, NULL);
+ if (ge != 0) {
+ r = -EIO;
+ goto finish;
+ }
+
+ ge = gcry_mpi_scan(&e, GCRYMPI_FMT_USG, exponent, exponent_size, NULL);
+ if (ge != 0) {
+ r = -EIO;
+ goto finish;
+ }
+
+ ge = gcry_mpi_scan(&n, GCRYMPI_FMT_USG, modulus, modulus_size, NULL);
+ if (ge != 0) {
+ r = -EIO;
+ goto finish;
+ }
+
+ ge = gcry_sexp_build(&signature_sexp,
+ NULL,
+ "(sig-val (rsa (s %m)))",
+ s);
+
+ if (ge != 0) {
+ r = -EIO;
+ goto finish;
+ }
+
+ ge = gcry_sexp_build(&data_sexp,
+ NULL,
+ "(data (flags pkcs1) (hash %s %b))",
+ hash_algorithm,
+ (int) data_size,
+ data);
+ if (ge != 0) {
+ r = -EIO;
+ goto finish;
+ }
+
+ ge = gcry_sexp_build(&public_key_sexp,
+ NULL,
+ "(public-key (rsa (n %m) (e %m)))",
+ n,
+ e);
+ if (ge != 0) {
+ r = -EIO;
+ goto finish;
+ }
+
+ ge = gcry_pk_verify(signature_sexp, data_sexp, public_key_sexp);
+ if (gpg_err_code(ge) == GPG_ERR_BAD_SIGNATURE)
+ r = 0;
+ else if (ge != 0) {
+ log_debug("RSA signature check failed: %s", gpg_strerror(ge));
+ r = -EIO;
+ } else
+ r = 1;
+
+finish:
+ if (e)
+ gcry_mpi_release(e);
+ if (n)
+ gcry_mpi_release(n);
+ if (s)
+ gcry_mpi_release(s);
+
+ if (public_key_sexp)
+ gcry_sexp_release(public_key_sexp);
+ if (signature_sexp)
+ gcry_sexp_release(signature_sexp);
+ if (data_sexp)
+ gcry_sexp_release(data_sexp);
+
+ return r;
+}
+
+static int dnssec_rsa_verify(
+ const char *hash_algorithm,
+ const void *hash, size_t hash_size,
+ DnsResourceRecord *rrsig,
+ DnsResourceRecord *dnskey) {
+
+ size_t exponent_size, modulus_size;
+ void *exponent, *modulus;
+
+ assert(hash_algorithm);
+ assert(hash);
+ assert(hash_size > 0);
+ assert(rrsig);
+ assert(dnskey);
+
+ if (*(uint8_t*) dnskey->dnskey.key == 0) {
+ /* exponent is > 255 bytes long */
+
+ exponent = (uint8_t*) dnskey->dnskey.key + 3;
+ exponent_size =
+ ((size_t) (((uint8_t*) dnskey->dnskey.key)[1]) << 8) |
+ ((size_t) ((uint8_t*) dnskey->dnskey.key)[2]);
+
+ if (exponent_size < 256)
+ return -EINVAL;
+
+ if (3 + exponent_size >= dnskey->dnskey.key_size)
+ return -EINVAL;
+
+ modulus = (uint8_t*) dnskey->dnskey.key + 3 + exponent_size;
+ modulus_size = dnskey->dnskey.key_size - 3 - exponent_size;
+
+ } else {
+ /* exponent is <= 255 bytes long */
+
+ exponent = (uint8_t*) dnskey->dnskey.key + 1;
+ exponent_size = (size_t) ((uint8_t*) dnskey->dnskey.key)[0];
+
+ if (exponent_size <= 0)
+ return -EINVAL;
+
+ if (1 + exponent_size >= dnskey->dnskey.key_size)
+ return -EINVAL;
+
+ modulus = (uint8_t*) dnskey->dnskey.key + 1 + exponent_size;
+ modulus_size = dnskey->dnskey.key_size - 1 - exponent_size;
+ }
+
+ return dnssec_rsa_verify_raw(
+ hash_algorithm,
+ rrsig->rrsig.signature, rrsig->rrsig.signature_size,
+ hash, hash_size,
+ exponent, exponent_size,
+ modulus, modulus_size);
+}
+
+static int dnssec_ecdsa_verify_raw(
+ const char *hash_algorithm,
+ const char *curve,
+ const void *signature_r, size_t signature_r_size,
+ const void *signature_s, size_t signature_s_size,
+ const void *data, size_t data_size,
+ const void *key, size_t key_size) {
+
+ gcry_sexp_t public_key_sexp = NULL, data_sexp = NULL, signature_sexp = NULL;
+ gcry_mpi_t q = NULL, r = NULL, s = NULL;
+ gcry_error_t ge;
+ int k;
+
+ assert(hash_algorithm);
+
+ ge = gcry_mpi_scan(&r, GCRYMPI_FMT_USG, signature_r, signature_r_size, NULL);
+ if (ge != 0) {
+ k = -EIO;
+ goto finish;
+ }
+
+ ge = gcry_mpi_scan(&s, GCRYMPI_FMT_USG, signature_s, signature_s_size, NULL);
+ if (ge != 0) {
+ k = -EIO;
+ goto finish;
+ }
+
+ ge = gcry_mpi_scan(&q, GCRYMPI_FMT_USG, key, key_size, NULL);
+ if (ge != 0) {
+ k = -EIO;
+ goto finish;
+ }
+
+ ge = gcry_sexp_build(&signature_sexp,
+ NULL,
+ "(sig-val (ecdsa (r %m) (s %m)))",
+ r,
+ s);
+ if (ge != 0) {
+ k = -EIO;
+ goto finish;
+ }
+
+ ge = gcry_sexp_build(&data_sexp,
+ NULL,
+ "(data (flags rfc6979) (hash %s %b))",
+ hash_algorithm,
+ (int) data_size,
+ data);
+ if (ge != 0) {
+ k = -EIO;
+ goto finish;
+ }
+
+ ge = gcry_sexp_build(&public_key_sexp,
+ NULL,
+ "(public-key (ecc (curve %s) (q %m)))",
+ curve,
+ q);
+ if (ge != 0) {
+ k = -EIO;
+ goto finish;
+ }
+
+ ge = gcry_pk_verify(signature_sexp, data_sexp, public_key_sexp);
+ if (gpg_err_code(ge) == GPG_ERR_BAD_SIGNATURE)
+ k = 0;
+ else if (ge != 0) {
+ log_debug("ECDSA signature check failed: %s", gpg_strerror(ge));
+ k = -EIO;
+ } else
+ k = 1;
+finish:
+ if (r)
+ gcry_mpi_release(r);
+ if (s)
+ gcry_mpi_release(s);
+ if (q)
+ gcry_mpi_release(q);
+
+ if (public_key_sexp)
+ gcry_sexp_release(public_key_sexp);
+ if (signature_sexp)
+ gcry_sexp_release(signature_sexp);
+ if (data_sexp)
+ gcry_sexp_release(data_sexp);
+
+ return k;
+}
+
+static int dnssec_ecdsa_verify(
+ const char *hash_algorithm,
+ int algorithm,
+ const void *hash, size_t hash_size,
+ DnsResourceRecord *rrsig,
+ DnsResourceRecord *dnskey) {
+
+ const char *curve;
+ size_t key_size;
+ uint8_t *q;
+
+ assert(hash);
+ assert(hash_size);
+ assert(rrsig);
+ assert(dnskey);
+
+ if (algorithm == DNSSEC_ALGORITHM_ECDSAP256SHA256) {
+ key_size = 32;
+ curve = "NIST P-256";
+ } else if (algorithm == DNSSEC_ALGORITHM_ECDSAP384SHA384) {
+ key_size = 48;
+ curve = "NIST P-384";
+ } else
+ return -EOPNOTSUPP;
+
+ if (dnskey->dnskey.key_size != key_size * 2)
+ return -EINVAL;
+
+ if (rrsig->rrsig.signature_size != key_size * 2)
+ return -EINVAL;
+
+ q = alloca(key_size*2 + 1);
+ q[0] = 0x04; /* Prepend 0x04 to indicate an uncompressed key */
+ memcpy(q+1, dnskey->dnskey.key, key_size*2);
+
+ return dnssec_ecdsa_verify_raw(
+ hash_algorithm,
+ curve,
+ rrsig->rrsig.signature, key_size,
+ (uint8_t*) rrsig->rrsig.signature + key_size, key_size,
+ hash, hash_size,
+ q, key_size*2+1);
+}
+
+static void md_add_uint8(gcry_md_hd_t md, uint8_t v) {
+ gcry_md_write(md, &v, sizeof(v));
+}
+
+static void md_add_uint16(gcry_md_hd_t md, uint16_t v) {
+ v = htobe16(v);
+ gcry_md_write(md, &v, sizeof(v));
+}
+
+static void md_add_uint32(gcry_md_hd_t md, uint32_t v) {
+ v = htobe32(v);
+ gcry_md_write(md, &v, sizeof(v));
+}
+
+static int dnssec_rrsig_prepare(DnsResourceRecord *rrsig) {
+ int n_key_labels, n_signer_labels;
+ const char *name;
+ int r;
+
+ /* Checks whether the specified RRSIG RR is somewhat valid, and initializes the .n_skip_labels_source and
+ * .n_skip_labels_signer fields so that we can use them later on. */
+
+ assert(rrsig);
+ assert(rrsig->key->type == DNS_TYPE_RRSIG);
+
+ /* Check if this RRSIG RR is already prepared */
+ if (rrsig->n_skip_labels_source != (unsigned) -1)
+ return 0;
+
+ if (rrsig->rrsig.inception > rrsig->rrsig.expiration)
+ return -EINVAL;
+
+ name = DNS_RESOURCE_KEY_NAME(rrsig->key);
+
+ n_key_labels = dns_name_count_labels(name);
+ if (n_key_labels < 0)
+ return n_key_labels;
+ if (rrsig->rrsig.labels > n_key_labels)
+ return -EINVAL;
+
+ n_signer_labels = dns_name_count_labels(rrsig->rrsig.signer);
+ if (n_signer_labels < 0)
+ return n_signer_labels;
+ if (n_signer_labels > rrsig->rrsig.labels)
+ return -EINVAL;
+
+ r = dns_name_skip(name, n_key_labels - n_signer_labels, &name);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EINVAL;
+
+ /* Check if the signer is really a suffix of us */
+ r = dns_name_equal(name, rrsig->rrsig.signer);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EINVAL;
+
+ rrsig->n_skip_labels_source = n_key_labels - rrsig->rrsig.labels;
+ rrsig->n_skip_labels_signer = n_key_labels - n_signer_labels;
+
+ return 0;
+}
+
+static int dnssec_rrsig_expired(DnsResourceRecord *rrsig, usec_t realtime) {
+ usec_t expiration, inception, skew;
+
+ assert(rrsig);
+ assert(rrsig->key->type == DNS_TYPE_RRSIG);
+
+ if (realtime == USEC_INFINITY)
+ realtime = now(CLOCK_REALTIME);
+
+ expiration = rrsig->rrsig.expiration * USEC_PER_SEC;
+ inception = rrsig->rrsig.inception * USEC_PER_SEC;
+
+ /* Consider inverted validity intervals as expired */
+ if (inception > expiration)
+ return true;
+
+ /* Permit a certain amount of clock skew of 10% of the valid
+ * time range. This takes inspiration from unbound's
+ * resolver. */
+ skew = (expiration - inception) / 10;
+ if (skew > SKEW_MAX)
+ skew = SKEW_MAX;
+
+ if (inception < skew)
+ inception = 0;
+ else
+ inception -= skew;
+
+ if (expiration + skew < expiration)
+ expiration = USEC_INFINITY;
+ else
+ expiration += skew;
+
+ return realtime < inception || realtime > expiration;
+}
+
+static int algorithm_to_gcrypt_md(uint8_t algorithm) {
+
+ /* Translates a DNSSEC signature algorithm into a gcrypt
+ * digest identifier.
+ *
+ * Note that we implement all algorithms listed as "Must
+ * implement" and "Recommended to Implement" in RFC6944. We
+ * don't implement any algorithms that are listed as
+ * "Optional" or "Must Not Implement". Specifically, we do not
+ * implement RSAMD5, DSASHA1, DH, DSA-NSEC3-SHA1, and
+ * GOST-ECC. */
+
+ switch (algorithm) {
+
+ case DNSSEC_ALGORITHM_RSASHA1:
+ case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1:
+ return GCRY_MD_SHA1;
+
+ case DNSSEC_ALGORITHM_RSASHA256:
+ case DNSSEC_ALGORITHM_ECDSAP256SHA256:
+ return GCRY_MD_SHA256;
+
+ case DNSSEC_ALGORITHM_ECDSAP384SHA384:
+ return GCRY_MD_SHA384;
+
+ case DNSSEC_ALGORITHM_RSASHA512:
+ return GCRY_MD_SHA512;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static void dnssec_fix_rrset_ttl(
+ DnsResourceRecord *list[],
+ unsigned n,
+ DnsResourceRecord *rrsig,
+ usec_t realtime) {
+
+ unsigned k;
+
+ assert(list);
+ assert(n > 0);
+ assert(rrsig);
+
+ for (k = 0; k < n; k++) {
+ DnsResourceRecord *rr = list[k];
+
+ /* Pick the TTL as the minimum of the RR's TTL, the
+ * RR's original TTL according to the RRSIG and the
+ * RRSIG's own TTL, see RFC 4035, Section 5.3.3 */
+ rr->ttl = MIN3(rr->ttl, rrsig->rrsig.original_ttl, rrsig->ttl);
+ rr->expiry = rrsig->rrsig.expiration * USEC_PER_SEC;
+
+ /* Copy over information about the signer and wildcard source of synthesis */
+ rr->n_skip_labels_source = rrsig->n_skip_labels_source;
+ rr->n_skip_labels_signer = rrsig->n_skip_labels_signer;
+ }
+
+ rrsig->expiry = rrsig->rrsig.expiration * USEC_PER_SEC;
+}
+
+int dnssec_verify_rrset(
+ DnsAnswer *a,
+ const DnsResourceKey *key,
+ DnsResourceRecord *rrsig,
+ DnsResourceRecord *dnskey,
+ usec_t realtime,
+ DnssecResult *result) {
+
+ uint8_t wire_format_name[DNS_WIRE_FOMAT_HOSTNAME_MAX];
+ DnsResourceRecord **list, *rr;
+ const char *source, *name;
+ gcry_md_hd_t md = NULL;
+ int r, md_algorithm;
+ size_t k, n = 0;
+ size_t hash_size;
+ void *hash;
+ bool wildcard;
+
+ assert(key);
+ assert(rrsig);
+ assert(dnskey);
+ assert(result);
+ assert(rrsig->key->type == DNS_TYPE_RRSIG);
+ assert(dnskey->key->type == DNS_TYPE_DNSKEY);
+
+ /* Verifies the the RRSet matching the specified "key" in "a",
+ * using the signature "rrsig" and the key "dnskey". It's
+ * assumed the RRSIG and DNSKEY match. */
+
+ md_algorithm = algorithm_to_gcrypt_md(rrsig->rrsig.algorithm);
+ if (md_algorithm == -EOPNOTSUPP) {
+ *result = DNSSEC_UNSUPPORTED_ALGORITHM;
+ return 0;
+ }
+ if (md_algorithm < 0)
+ return md_algorithm;
+
+ r = dnssec_rrsig_prepare(rrsig);
+ if (r == -EINVAL) {
+ *result = DNSSEC_INVALID;
+ return r;
+ }
+ if (r < 0)
+ return r;
+
+ r = dnssec_rrsig_expired(rrsig, realtime);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ *result = DNSSEC_SIGNATURE_EXPIRED;
+ return 0;
+ }
+
+ name = DNS_RESOURCE_KEY_NAME(key);
+
+ /* Some keys may only appear signed in the zone apex, and are invalid anywhere else. (SOA, NS...) */
+ if (dns_type_apex_only(rrsig->rrsig.type_covered)) {
+ r = dns_name_equal(rrsig->rrsig.signer, name);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ *result = DNSSEC_INVALID;
+ return 0;
+ }
+ }
+
+ /* OTOH DS RRs may not appear in the zone apex, but are valid everywhere else. */
+ if (rrsig->rrsig.type_covered == DNS_TYPE_DS) {
+ r = dns_name_equal(rrsig->rrsig.signer, name);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ *result = DNSSEC_INVALID;
+ return 0;
+ }
+ }
+
+ /* Determine the "Source of Synthesis" and whether this is a wildcard RRSIG */
+ r = dns_name_suffix(name, rrsig->rrsig.labels, &source);
+ if (r < 0)
+ return r;
+ if (r > 0 && !dns_type_may_wildcard(rrsig->rrsig.type_covered)) {
+ /* We refuse to validate NSEC3 or SOA RRs that are synthesized from wildcards */
+ *result = DNSSEC_INVALID;
+ return 0;
+ }
+ if (r == 1) {
+ /* If we stripped a single label, then let's see if that maybe was "*". If so, we are not really
+ * synthesized from a wildcard, we are the wildcard itself. Treat that like a normal name. */
+ r = dns_name_startswith(name, "*");
+ if (r < 0)
+ return r;
+ if (r > 0)
+ source = name;
+
+ wildcard = r == 0;
+ } else
+ wildcard = r > 0;
+
+ /* Collect all relevant RRs in a single array, so that we can look at the RRset */
+ list = newa(DnsResourceRecord *, dns_answer_size(a));
+
+ DNS_ANSWER_FOREACH(rr, a) {
+ r = dns_resource_key_equal(key, rr->key);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ /* We need the wire format for ordering, and digest calculation */
+ r = dns_resource_record_to_wire_format(rr, true);
+ if (r < 0)
+ return r;
+
+ list[n++] = rr;
+
+ if (n > VERIFY_RRS_MAX)
+ return -E2BIG;
+ }
+
+ if (n <= 0)
+ return -ENODATA;
+
+ /* Bring the RRs into canonical order */
+ qsort_safe(list, n, sizeof(DnsResourceRecord*), rr_compare);
+
+ /* OK, the RRs are now in canonical order. Let's calculate the digest */
+ initialize_libgcrypt();
+
+ hash_size = gcry_md_get_algo_dlen(md_algorithm);
+ assert(hash_size > 0);
+
+ gcry_md_open(&md, md_algorithm, 0);
+ if (!md)
+ return -EIO;
+
+ md_add_uint16(md, rrsig->rrsig.type_covered);
+ md_add_uint8(md, rrsig->rrsig.algorithm);
+ md_add_uint8(md, rrsig->rrsig.labels);
+ md_add_uint32(md, rrsig->rrsig.original_ttl);
+ md_add_uint32(md, rrsig->rrsig.expiration);
+ md_add_uint32(md, rrsig->rrsig.inception);
+ md_add_uint16(md, rrsig->rrsig.key_tag);
+
+ r = dns_name_to_wire_format(rrsig->rrsig.signer, wire_format_name, sizeof(wire_format_name), true);
+ if (r < 0)
+ goto finish;
+ gcry_md_write(md, wire_format_name, r);
+
+ /* Convert the source of synthesis into wire format */
+ r = dns_name_to_wire_format(source, wire_format_name, sizeof(wire_format_name), true);
+ if (r < 0)
+ goto finish;
+
+ for (k = 0; k < n; k++) {
+ size_t l;
+
+ rr = list[k];
+
+ /* Hash the source of synthesis. If this is a wildcard, then prefix it with the *. label */
+ if (wildcard)
+ gcry_md_write(md, (uint8_t[]) { 1, '*'}, 2);
+ gcry_md_write(md, wire_format_name, r);
+
+ md_add_uint16(md, rr->key->type);
+ md_add_uint16(md, rr->key->class);
+ md_add_uint32(md, rrsig->rrsig.original_ttl);
+
+ l = DNS_RESOURCE_RECORD_RDATA_SIZE(rr);
+ assert(l <= 0xFFFF);
+
+ md_add_uint16(md, (uint16_t) l);
+ gcry_md_write(md, DNS_RESOURCE_RECORD_RDATA(rr), l);
+ }
+
+ hash = gcry_md_read(md, 0);
+ if (!hash) {
+ r = -EIO;
+ goto finish;
+ }
+
+ switch (rrsig->rrsig.algorithm) {
+
+ case DNSSEC_ALGORITHM_RSASHA1:
+ case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1:
+ case DNSSEC_ALGORITHM_RSASHA256:
+ case DNSSEC_ALGORITHM_RSASHA512:
+ r = dnssec_rsa_verify(
+ gcry_md_algo_name(md_algorithm),
+ hash, hash_size,
+ rrsig,
+ dnskey);
+ break;
+
+ case DNSSEC_ALGORITHM_ECDSAP256SHA256:
+ case DNSSEC_ALGORITHM_ECDSAP384SHA384:
+ r = dnssec_ecdsa_verify(
+ gcry_md_algo_name(md_algorithm),
+ rrsig->rrsig.algorithm,
+ hash, hash_size,
+ rrsig,
+ dnskey);
+ break;
+ }
+
+ if (r < 0)
+ goto finish;
+
+ /* Now, fix the ttl, expiry, and remember the synthesizing source and the signer */
+ if (r > 0)
+ dnssec_fix_rrset_ttl(list, n, rrsig, realtime);
+
+ if (r == 0)
+ *result = DNSSEC_INVALID;
+ else if (wildcard)
+ *result = DNSSEC_VALIDATED_WILDCARD;
+ else
+ *result = DNSSEC_VALIDATED;
+
+ r = 0;
+
+finish:
+ gcry_md_close(md);
+ return r;
+}
+
+int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, bool revoked_ok) {
+
+ assert(rrsig);
+ assert(dnskey);
+
+ /* Checks if the specified DNSKEY RR matches the key used for
+ * the signature in the specified RRSIG RR */
+
+ if (rrsig->key->type != DNS_TYPE_RRSIG)
+ return -EINVAL;
+
+ if (dnskey->key->type != DNS_TYPE_DNSKEY)
+ return 0;
+ if (dnskey->key->class != rrsig->key->class)
+ return 0;
+ if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
+ return 0;
+ if (!revoked_ok && (dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE))
+ return 0;
+ if (dnskey->dnskey.protocol != 3)
+ return 0;
+ if (dnskey->dnskey.algorithm != rrsig->rrsig.algorithm)
+ return 0;
+
+ if (dnssec_keytag(dnskey, false) != rrsig->rrsig.key_tag)
+ return 0;
+
+ return dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), rrsig->rrsig.signer);
+}
+
+int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig) {
+ assert(key);
+ assert(rrsig);
+
+ /* Checks if the specified RRSIG RR protects the RRSet of the specified RR key. */
+
+ if (rrsig->key->type != DNS_TYPE_RRSIG)
+ return 0;
+ if (rrsig->key->class != key->class)
+ return 0;
+ if (rrsig->rrsig.type_covered != key->type)
+ return 0;
+
+ return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig->key), DNS_RESOURCE_KEY_NAME(key));
+}
+
+int dnssec_verify_rrset_search(
+ DnsAnswer *a,
+ const DnsResourceKey *key,
+ DnsAnswer *validated_dnskeys,
+ usec_t realtime,
+ DnssecResult *result,
+ DnsResourceRecord **ret_rrsig) {
+
+ bool found_rrsig = false, found_invalid = false, found_expired_rrsig = false, found_unsupported_algorithm = false;
+ DnsResourceRecord *rrsig;
+ int r;
+
+ assert(key);
+ assert(result);
+
+ /* Verifies all RRs from "a" that match the key "key" against DNSKEYs in "validated_dnskeys" */
+
+ if (!a || a->n_rrs <= 0)
+ return -ENODATA;
+
+ /* Iterate through each RRSIG RR. */
+ DNS_ANSWER_FOREACH(rrsig, a) {
+ DnsResourceRecord *dnskey;
+ DnsAnswerFlags flags;
+
+ /* Is this an RRSIG RR that applies to RRs matching our key? */
+ r = dnssec_key_match_rrsig(key, rrsig);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ found_rrsig = true;
+
+ /* Look for a matching key */
+ DNS_ANSWER_FOREACH_FLAGS(dnskey, flags, validated_dnskeys) {
+ DnssecResult one_result;
+
+ if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
+ continue;
+
+ /* Is this a DNSKEY RR that matches they key of our RRSIG? */
+ r = dnssec_rrsig_match_dnskey(rrsig, dnskey, false);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ /* Take the time here, if it isn't set yet, so
+ * that we do all validations with the same
+ * time. */
+ if (realtime == USEC_INFINITY)
+ realtime = now(CLOCK_REALTIME);
+
+ /* Yay, we found a matching RRSIG with a matching
+ * DNSKEY, awesome. Now let's verify all entries of
+ * the RRSet against the RRSIG and DNSKEY
+ * combination. */
+
+ r = dnssec_verify_rrset(a, key, rrsig, dnskey, realtime, &one_result);
+ if (r < 0)
+ return r;
+
+ switch (one_result) {
+
+ case DNSSEC_VALIDATED:
+ case DNSSEC_VALIDATED_WILDCARD:
+ /* Yay, the RR has been validated,
+ * return immediately, but fix up the expiry */
+ if (ret_rrsig)
+ *ret_rrsig = rrsig;
+
+ *result = one_result;
+ return 0;
+
+ case DNSSEC_INVALID:
+ /* If the signature is invalid, let's try another
+ key and/or signature. After all they
+ key_tags and stuff are not unique, and
+ might be shared by multiple keys. */
+ found_invalid = true;
+ continue;
+
+ case DNSSEC_UNSUPPORTED_ALGORITHM:
+ /* If the key algorithm is
+ unsupported, try another
+ RRSIG/DNSKEY pair, but remember we
+ encountered this, so that we can
+ return a proper error when we
+ encounter nothing better. */
+ found_unsupported_algorithm = true;
+ continue;
+
+ case DNSSEC_SIGNATURE_EXPIRED:
+ /* If the signature is expired, try
+ another one, but remember it, so
+ that we can return this */
+ found_expired_rrsig = true;
+ continue;
+
+ default:
+ assert_not_reached("Unexpected DNSSEC validation result");
+ }
+ }
+ }
+
+ if (found_expired_rrsig)
+ *result = DNSSEC_SIGNATURE_EXPIRED;
+ else if (found_unsupported_algorithm)
+ *result = DNSSEC_UNSUPPORTED_ALGORITHM;
+ else if (found_invalid)
+ *result = DNSSEC_INVALID;
+ else if (found_rrsig)
+ *result = DNSSEC_MISSING_KEY;
+ else
+ *result = DNSSEC_NO_SIGNATURE;
+
+ if (ret_rrsig)
+ *ret_rrsig = NULL;
+
+ return 0;
+}
+
+int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key) {
+ DnsResourceRecord *rr;
+ int r;
+
+ /* Checks whether there's at least one RRSIG in 'a' that proctects RRs of the specified key */
+
+ DNS_ANSWER_FOREACH(rr, a) {
+ r = dnssec_key_match_rrsig(key, rr);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
+ size_t c = 0;
+ int r;
+
+ /* Converts the specified hostname into DNSSEC canonicalized
+ * form. */
+
+ if (buffer_max < 2)
+ return -ENOBUFS;
+
+ for (;;) {
+ r = dns_label_unescape(&n, buffer, buffer_max);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ if (buffer_max < (size_t) r + 2)
+ return -ENOBUFS;
+
+ /* The DNSSEC canonical form is not clear on what to
+ * do with dots appearing in labels, the way DNS-SD
+ * does it. Refuse it for now. */
+
+ if (memchr(buffer, '.', r))
+ return -EINVAL;
+
+ ascii_strlower_n(buffer, (size_t) r);
+ buffer[r] = '.';
+
+ buffer += r + 1;
+ c += r + 1;
+
+ buffer_max -= r + 1;
+ }
+
+ if (c <= 0) {
+ /* Not even a single label: this is the root domain name */
+
+ assert(buffer_max > 2);
+ buffer[0] = '.';
+ buffer[1] = 0;
+
+ return 1;
+ }
+
+ return (int) c;
+}
+
+static int digest_to_gcrypt_md(uint8_t algorithm) {
+
+ /* Translates a DNSSEC digest algorithm into a gcrypt digest identifier */
+
+ switch (algorithm) {
+
+ case DNSSEC_DIGEST_SHA1:
+ return GCRY_MD_SHA1;
+
+ case DNSSEC_DIGEST_SHA256:
+ return GCRY_MD_SHA256;
+
+ case DNSSEC_DIGEST_SHA384:
+ return GCRY_MD_SHA384;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke) {
+ char owner_name[DNSSEC_CANONICAL_HOSTNAME_MAX];
+ gcry_md_hd_t md = NULL;
+ size_t hash_size;
+ int md_algorithm, r;
+ void *result;
+
+ assert(dnskey);
+ assert(ds);
+
+ /* Implements DNSKEY verification by a DS, according to RFC 4035, section 5.2 */
+
+ if (dnskey->key->type != DNS_TYPE_DNSKEY)
+ return -EINVAL;
+ if (ds->key->type != DNS_TYPE_DS)
+ return -EINVAL;
+ if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
+ return -EKEYREJECTED;
+ if (!mask_revoke && (dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE))
+ return -EKEYREJECTED;
+ if (dnskey->dnskey.protocol != 3)
+ return -EKEYREJECTED;
+
+ if (dnskey->dnskey.algorithm != ds->ds.algorithm)
+ return 0;
+ if (dnssec_keytag(dnskey, mask_revoke) != ds->ds.key_tag)
+ return 0;
+
+ initialize_libgcrypt();
+
+ md_algorithm = digest_to_gcrypt_md(ds->ds.digest_type);
+ if (md_algorithm < 0)
+ return md_algorithm;
+
+ hash_size = gcry_md_get_algo_dlen(md_algorithm);
+ assert(hash_size > 0);
+
+ if (ds->ds.digest_size != hash_size)
+ return 0;
+
+ r = dnssec_canonicalize(DNS_RESOURCE_KEY_NAME(dnskey->key), owner_name, sizeof(owner_name));
+ if (r < 0)
+ return r;
+
+ gcry_md_open(&md, md_algorithm, 0);
+ if (!md)
+ return -EIO;
+
+ gcry_md_write(md, owner_name, r);
+ if (mask_revoke)
+ md_add_uint16(md, dnskey->dnskey.flags & ~DNSKEY_FLAG_REVOKE);
+ else
+ md_add_uint16(md, dnskey->dnskey.flags);
+ md_add_uint8(md, dnskey->dnskey.protocol);
+ md_add_uint8(md, dnskey->dnskey.algorithm);
+ gcry_md_write(md, dnskey->dnskey.key, dnskey->dnskey.key_size);
+
+ result = gcry_md_read(md, 0);
+ if (!result) {
+ r = -EIO;
+ goto finish;
+ }
+
+ r = memcmp(result, ds->ds.digest, ds->ds.digest_size) != 0;
+
+finish:
+ gcry_md_close(md);
+ return r;
+}
+
+int dnssec_verify_dnskey_by_ds_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds) {
+ DnsResourceRecord *ds;
+ DnsAnswerFlags flags;
+ int r;
+
+ assert(dnskey);
+
+ if (dnskey->key->type != DNS_TYPE_DNSKEY)
+ return 0;
+
+ DNS_ANSWER_FOREACH_FLAGS(ds, flags, validated_ds) {
+
+ if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
+ continue;
+
+ if (ds->key->type != DNS_TYPE_DS)
+ continue;
+ if (ds->key->class != dnskey->key->class)
+ continue;
+
+ r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), DNS_RESOURCE_KEY_NAME(ds->key));
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ r = dnssec_verify_dnskey_by_ds(dnskey, ds, false);
+ if (IN_SET(r, -EKEYREJECTED, -EOPNOTSUPP))
+ return 0; /* The DNSKEY is revoked or otherwise invalid, or we don't support the digest algorithm */
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+static int nsec3_hash_to_gcrypt_md(uint8_t algorithm) {
+
+ /* Translates a DNSSEC NSEC3 hash algorithm into a gcrypt digest identifier */
+
+ switch (algorithm) {
+
+ case NSEC3_ALGORITHM_SHA1:
+ return GCRY_MD_SHA1;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) {
+ uint8_t wire_format[DNS_WIRE_FOMAT_HOSTNAME_MAX];
+ gcry_md_hd_t md = NULL;
+ size_t hash_size;
+ int algorithm;
+ void *result;
+ unsigned k;
+ int r;
+
+ assert(nsec3);
+ assert(name);
+ assert(ret);
+
+ if (nsec3->key->type != DNS_TYPE_NSEC3)
+ return -EINVAL;
+
+ if (nsec3->nsec3.iterations > NSEC3_ITERATIONS_MAX) {
+ log_debug("Ignoring NSEC3 RR %s with excessive number of iterations.", dns_resource_record_to_string(nsec3));
+ return -EOPNOTSUPP;
+ }
+
+ algorithm = nsec3_hash_to_gcrypt_md(nsec3->nsec3.algorithm);
+ if (algorithm < 0)
+ return algorithm;
+
+ initialize_libgcrypt();
+
+ hash_size = gcry_md_get_algo_dlen(algorithm);
+ assert(hash_size > 0);
+
+ if (nsec3->nsec3.next_hashed_name_size != hash_size)
+ return -EINVAL;
+
+ r = dns_name_to_wire_format(name, wire_format, sizeof(wire_format), true);
+ if (r < 0)
+ return r;
+
+ gcry_md_open(&md, algorithm, 0);
+ if (!md)
+ return -EIO;
+
+ gcry_md_write(md, wire_format, r);
+ gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
+
+ result = gcry_md_read(md, 0);
+ if (!result) {
+ r = -EIO;
+ goto finish;
+ }
+
+ for (k = 0; k < nsec3->nsec3.iterations; k++) {
+ uint8_t tmp[hash_size];
+ memcpy(tmp, result, hash_size);
+
+ gcry_md_reset(md);
+ gcry_md_write(md, tmp, hash_size);
+ gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
+
+ result = gcry_md_read(md, 0);
+ if (!result) {
+ r = -EIO;
+ goto finish;
+ }
+ }
+
+ memcpy(ret, result, hash_size);
+ r = (int) hash_size;
+
+finish:
+ gcry_md_close(md);
+ return r;
+}
+
+static int nsec3_is_good(DnsResourceRecord *rr, DnsResourceRecord *nsec3) {
+ const char *a, *b;
+ int r;
+
+ assert(rr);
+
+ if (rr->key->type != DNS_TYPE_NSEC3)
+ return 0;
+
+ /* RFC 5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
+ if (!IN_SET(rr->nsec3.flags, 0, 1))
+ return 0;
+
+ /* Ignore NSEC3 RRs whose algorithm we don't know */
+ if (nsec3_hash_to_gcrypt_md(rr->nsec3.algorithm) < 0)
+ return 0;
+ /* Ignore NSEC3 RRs with an excessive number of required iterations */
+ if (rr->nsec3.iterations > NSEC3_ITERATIONS_MAX)
+ return 0;
+
+ /* Ignore NSEC3 RRs generated from wildcards */
+ if (rr->n_skip_labels_source != 0)
+ return 0;
+ /* Ignore NSEC3 RRs that are located anywhere else than one label below the zone */
+ if (rr->n_skip_labels_signer != 1)
+ return 0;
+
+ if (!nsec3)
+ return 1;
+
+ /* If a second NSEC3 RR is specified, also check if they are from the same zone. */
+
+ if (nsec3 == rr) /* Shortcut */
+ return 1;
+
+ if (rr->key->class != nsec3->key->class)
+ return 0;
+ if (rr->nsec3.algorithm != nsec3->nsec3.algorithm)
+ return 0;
+ if (rr->nsec3.iterations != nsec3->nsec3.iterations)
+ return 0;
+ if (rr->nsec3.salt_size != nsec3->nsec3.salt_size)
+ return 0;
+ if (memcmp(rr->nsec3.salt, nsec3->nsec3.salt, rr->nsec3.salt_size) != 0)
+ return 0;
+
+ a = DNS_RESOURCE_KEY_NAME(rr->key);
+ r = dns_name_parent(&a); /* strip off hash */
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 0;
+
+ b = DNS_RESOURCE_KEY_NAME(nsec3->key);
+ r = dns_name_parent(&b); /* strip off hash */
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 0;
+
+ /* Make sure both have the same parent */
+ return dns_name_equal(a, b);
+}
+
+static int nsec3_hashed_domain_format(const uint8_t *hashed, size_t hashed_size, const char *zone, char **ret) {
+ _cleanup_free_ char *l = NULL;
+ char *j;
+
+ assert(hashed);
+ assert(hashed_size > 0);
+ assert(zone);
+ assert(ret);
+
+ l = base32hexmem(hashed, hashed_size, false);
+ if (!l)
+ return -ENOMEM;
+
+ j = strjoin(l, ".", zone, NULL);
+ if (!j)
+ return -ENOMEM;
+
+ *ret = j;
+ return (int) hashed_size;
+}
+
+static int nsec3_hashed_domain_make(DnsResourceRecord *nsec3, const char *domain, const char *zone, char **ret) {
+ uint8_t hashed[DNSSEC_HASH_SIZE_MAX];
+ int hashed_size;
+
+ assert(nsec3);
+ assert(domain);
+ assert(zone);
+ assert(ret);
+
+ hashed_size = dnssec_nsec3_hash(nsec3, domain, hashed);
+ if (hashed_size < 0)
+ return hashed_size;
+
+ return nsec3_hashed_domain_format(hashed, (size_t) hashed_size, zone, ret);
+}
+
+/* See RFC 5155, Section 8
+ * First try to find a NSEC3 record that matches our query precisely, if that fails, find the closest
+ * enclosure. Secondly, find a proof that there is no closer enclosure and either a proof that there
+ * is no wildcard domain as a direct descendant of the closest enclosure, or find an NSEC3 record that
+ * matches the wildcard domain.
+ *
+ * Based on this we can prove either the existence of the record in @key, or NXDOMAIN or NODATA, or
+ * that there is no proof either way. The latter is the case if a the proof of non-existence of a given
+ * name uses an NSEC3 record with the opt-out bit set. Lastly, if we are given insufficient NSEC3 records
+ * to conclude anything we indicate this by returning NO_RR. */
+static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) {
+ _cleanup_free_ char *next_closer_domain = NULL, *wildcard_domain = NULL;
+ const char *zone, *p, *pp = NULL, *wildcard;
+ DnsResourceRecord *rr, *enclosure_rr, *zone_rr, *wildcard_rr = NULL;
+ DnsAnswerFlags flags;
+ int hashed_size, r;
+ bool a, no_closer = false, no_wildcard = false, optout = false;
+
+ assert(key);
+ assert(result);
+
+ /* First step, find the zone name and the NSEC3 parameters of the zone.
+ * it is sufficient to look for the longest common suffix we find with
+ * any NSEC3 RR in the response. Any NSEC3 record will do as all NSEC3
+ * records from a given zone in a response must use the same
+ * parameters. */
+ zone = DNS_RESOURCE_KEY_NAME(key);
+ for (;;) {
+ DNS_ANSWER_FOREACH_FLAGS(zone_rr, flags, answer) {
+ r = nsec3_is_good(zone_rr, NULL);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ r = dns_name_equal_skip(DNS_RESOURCE_KEY_NAME(zone_rr->key), 1, zone);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ goto found_zone;
+ }
+
+ /* Strip one label from the front */
+ r = dns_name_parent(&zone);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+ }
+
+ *result = DNSSEC_NSEC_NO_RR;
+ return 0;
+
+found_zone:
+ /* Second step, find the closest encloser NSEC3 RR in 'answer' that matches 'key' */
+ p = DNS_RESOURCE_KEY_NAME(key);
+ for (;;) {
+ _cleanup_free_ char *hashed_domain = NULL;
+
+ hashed_size = nsec3_hashed_domain_make(zone_rr, p, zone, &hashed_domain);
+ if (hashed_size == -EOPNOTSUPP) {
+ *result = DNSSEC_NSEC_UNSUPPORTED_ALGORITHM;
+ return 0;
+ }
+ if (hashed_size < 0)
+ return hashed_size;
+
+ DNS_ANSWER_FOREACH_FLAGS(enclosure_rr, flags, answer) {
+
+ r = nsec3_is_good(enclosure_rr, zone_rr);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ if (enclosure_rr->nsec3.next_hashed_name_size != (size_t) hashed_size)
+ continue;
+
+ r = dns_name_equal(DNS_RESOURCE_KEY_NAME(enclosure_rr->key), hashed_domain);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ a = flags & DNS_ANSWER_AUTHENTICATED;
+ goto found_closest_encloser;
+ }
+ }
+
+ /* We didn't find the closest encloser with this name,
+ * but let's remember this domain name, it might be
+ * the next closer name */
+
+ pp = p;
+
+ /* Strip one label from the front */
+ r = dns_name_parent(&p);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+ }
+
+ *result = DNSSEC_NSEC_NO_RR;
+ return 0;
+
+found_closest_encloser:
+ /* We found a closest encloser in 'p'; next closer is 'pp' */
+
+ /* Ensure this is not a DNAME domain, see RFC5155, section 8.3. */
+ if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_DNAME))
+ return -EBADMSG;
+
+ /* Ensure that this data is from the delegated domain
+ * (i.e. originates from the "lower" DNS server), and isn't
+ * just glue records (i.e. doesn't originate from the "upper"
+ * DNS server). */
+ if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_NS) &&
+ !bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA))
+ return -EBADMSG;
+
+ if (!pp) {
+ /* No next closer NSEC3 RR. That means there's a direct NSEC3 RR for our key. */
+ if (bitmap_isset(enclosure_rr->nsec3.types, key->type))
+ *result = DNSSEC_NSEC_FOUND;
+ else if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_CNAME))
+ *result = DNSSEC_NSEC_CNAME;
+ else
+ *result = DNSSEC_NSEC_NODATA;
+
+ if (authenticated)
+ *authenticated = a;
+ if (ttl)
+ *ttl = enclosure_rr->ttl;
+
+ return 0;
+ }
+
+ /* Prove that there is no next closer and whether or not there is a wildcard domain. */
+
+ wildcard = strjoina("*.", p);
+ r = nsec3_hashed_domain_make(enclosure_rr, wildcard, zone, &wildcard_domain);
+ if (r < 0)
+ return r;
+ if (r != hashed_size)
+ return -EBADMSG;
+
+ r = nsec3_hashed_domain_make(enclosure_rr, pp, zone, &next_closer_domain);
+ if (r < 0)
+ return r;
+ if (r != hashed_size)
+ return -EBADMSG;
+
+ DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
+ _cleanup_free_ char *next_hashed_domain = NULL;
+
+ r = nsec3_is_good(rr, zone_rr);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ r = nsec3_hashed_domain_format(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, zone, &next_hashed_domain);
+ if (r < 0)
+ return r;
+
+ r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), next_closer_domain, next_hashed_domain);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ if (rr->nsec3.flags & 1)
+ optout = true;
+
+ a = a && (flags & DNS_ANSWER_AUTHENTICATED);
+
+ no_closer = true;
+ }
+
+ r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), wildcard_domain);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ a = a && (flags & DNS_ANSWER_AUTHENTICATED);
+
+ wildcard_rr = rr;
+ }
+
+ r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), wildcard_domain, next_hashed_domain);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ if (rr->nsec3.flags & 1)
+ /* This only makes sense if we have a wildcard delegation, which is
+ * very unlikely, see RFC 4592, Section 4.2, but we cannot rely on
+ * this not happening, so hence cannot simply conclude NXDOMAIN as
+ * we would wish */
+ optout = true;
+
+ a = a && (flags & DNS_ANSWER_AUTHENTICATED);
+
+ no_wildcard = true;
+ }
+ }
+
+ if (wildcard_rr && no_wildcard)
+ return -EBADMSG;
+
+ if (!no_closer) {
+ *result = DNSSEC_NSEC_NO_RR;
+ return 0;
+ }
+
+ if (wildcard_rr) {
+ /* A wildcard exists that matches our query. */
+ if (optout)
+ /* This is not specified in any RFC to the best of my knowledge, but
+ * if the next closer enclosure is covered by an opt-out NSEC3 RR
+ * it means that we cannot prove that the source of synthesis is
+ * correct, as there may be a closer match. */
+ *result = DNSSEC_NSEC_OPTOUT;
+ else if (bitmap_isset(wildcard_rr->nsec3.types, key->type))
+ *result = DNSSEC_NSEC_FOUND;
+ else if (bitmap_isset(wildcard_rr->nsec3.types, DNS_TYPE_CNAME))
+ *result = DNSSEC_NSEC_CNAME;
+ else
+ *result = DNSSEC_NSEC_NODATA;
+ } else {
+ if (optout)
+ /* The RFC only specifies that we have to care for optout for NODATA for
+ * DS records. However, children of an insecure opt-out delegation should
+ * also be considered opt-out, rather than verified NXDOMAIN.
+ * Note that we do not require a proof of wildcard non-existence if the
+ * next closer domain is covered by an opt-out, as that would not provide
+ * any additional information. */
+ *result = DNSSEC_NSEC_OPTOUT;
+ else if (no_wildcard)
+ *result = DNSSEC_NSEC_NXDOMAIN;
+ else {
+ *result = DNSSEC_NSEC_NO_RR;
+
+ return 0;
+ }
+ }
+
+ if (authenticated)
+ *authenticated = a;
+
+ if (ttl)
+ *ttl = enclosure_rr->ttl;
+
+ return 0;
+}
+
+static int dnssec_nsec_wildcard_equal(DnsResourceRecord *rr, const char *name) {
+ char label[DNS_LABEL_MAX];
+ const char *n;
+ int r;
+
+ assert(rr);
+ assert(rr->key->type == DNS_TYPE_NSEC);
+
+ /* Checks whether the specified RR has a name beginning in "*.", and if the rest is a suffix of our name */
+
+ if (rr->n_skip_labels_source != 1)
+ return 0;
+
+ n = DNS_RESOURCE_KEY_NAME(rr->key);
+ r = dns_label_unescape(&n, label, sizeof(label));
+ if (r <= 0)
+ return r;
+ if (r != 1 || label[0] != '*')
+ return 0;
+
+ return dns_name_endswith(name, n);
+}
+
+static int dnssec_nsec_in_path(DnsResourceRecord *rr, const char *name) {
+ const char *nn, *common_suffix;
+ int r;
+
+ assert(rr);
+ assert(rr->key->type == DNS_TYPE_NSEC);
+
+ /* Checks whether the specified nsec RR indicates that name is an empty non-terminal (ENT)
+ *
+ * A couple of examples:
+ *
+ * NSEC bar → waldo.foo.bar: indicates that foo.bar exists and is an ENT
+ * NSEC waldo.foo.bar → yyy.zzz.xoo.bar: indicates that xoo.bar and zzz.xoo.bar exist and are ENTs
+ * NSEC yyy.zzz.xoo.bar → bar: indicates pretty much nothing about ENTs
+ */
+
+ /* First, determine parent of next domain. */
+ nn = rr->nsec.next_domain_name;
+ r = dns_name_parent(&nn);
+ if (r <= 0)
+ return r;
+
+ /* If the name we just determined is not equal or child of the name we are interested in, then we can't say
+ * anything at all. */
+ r = dns_name_endswith(nn, name);
+ if (r <= 0)
+ return r;
+
+ /* If the name we we are interested in is not a prefix of the common suffix of the NSEC RR's owner and next domain names, then we can't say anything either. */
+ r = dns_name_common_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rr->nsec.next_domain_name, &common_suffix);
+ if (r < 0)
+ return r;
+
+ return dns_name_endswith(name, common_suffix);
+}
+
+static int dnssec_nsec_from_parent_zone(DnsResourceRecord *rr, const char *name) {
+ int r;
+
+ assert(rr);
+ assert(rr->key->type == DNS_TYPE_NSEC);
+
+ /* Checks whether this NSEC originates to the parent zone or the child zone. */
+
+ r = dns_name_parent(&name);
+ if (r <= 0)
+ return r;
+
+ r = dns_name_equal(name, DNS_RESOURCE_KEY_NAME(rr->key));
+ if (r <= 0)
+ return r;
+
+ /* DNAME, and NS without SOA is an indication for a delegation. */
+ if (bitmap_isset(rr->nsec.types, DNS_TYPE_DNAME))
+ return 1;
+
+ if (bitmap_isset(rr->nsec.types, DNS_TYPE_NS) && !bitmap_isset(rr->nsec.types, DNS_TYPE_SOA))
+ return 1;
+
+ return 0;
+}
+
+static int dnssec_nsec_covers(DnsResourceRecord *rr, const char *name) {
+ const char *common_suffix, *p;
+ int r;
+
+ assert(rr);
+ assert(rr->key->type == DNS_TYPE_NSEC);
+
+ /* Checks whether the "Next Closer" is witin the space covered by the specified RR. */
+
+ r = dns_name_common_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rr->nsec.next_domain_name, &common_suffix);
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ p = name;
+ r = dns_name_parent(&name);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 0;
+
+ r = dns_name_equal(name, common_suffix);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ break;
+ }
+
+ /* p is now the "Next Closer". */
+
+ return dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), p, rr->nsec.next_domain_name);
+}
+
+static int dnssec_nsec_covers_wildcard(DnsResourceRecord *rr, const char *name) {
+ const char *common_suffix, *wc;
+ int r;
+
+ assert(rr);
+ assert(rr->key->type == DNS_TYPE_NSEC);
+
+ /* Checks whether the "Wildcard at the Closest Encloser" is within the space covered by the specified
+ * RR. Specifically, checks whether 'name' has the common suffix of the NSEC RR's owner and next names as
+ * suffix, and whether the NSEC covers the name generated by that suffix prepended with an asterisk label.
+ *
+ * NSEC bar → waldo.foo.bar: indicates that *.bar and *.foo.bar do not exist
+ * NSEC waldo.foo.bar → yyy.zzz.xoo.bar: indicates that *.xoo.bar and *.zzz.xoo.bar do not exist (and more ...)
+ * NSEC yyy.zzz.xoo.bar → bar: indicates that a number of wildcards don#t exist either...
+ */
+
+ r = dns_name_common_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rr->nsec.next_domain_name, &common_suffix);
+ if (r < 0)
+ return r;
+
+ /* If the common suffix is not shared by the name we are interested in, it has nothing to say for us. */
+ r = dns_name_endswith(name, common_suffix);
+ if (r <= 0)
+ return r;
+
+ wc = strjoina("*.", common_suffix, NULL);
+ return dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), wc, rr->nsec.next_domain_name);
+}
+
+int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) {
+ bool have_nsec3 = false, covering_rr_authenticated = false, wildcard_rr_authenticated = false;
+ DnsResourceRecord *rr, *covering_rr = NULL, *wildcard_rr = NULL;
+ DnsAnswerFlags flags;
+ const char *name;
+ int r;
+
+ assert(key);
+ assert(result);
+
+ /* Look for any NSEC/NSEC3 RRs that say something about the specified key. */
+
+ name = DNS_RESOURCE_KEY_NAME(key);
+
+ DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
+
+ if (rr->key->class != key->class)
+ continue;
+
+ have_nsec3 = have_nsec3 || (rr->key->type == DNS_TYPE_NSEC3);
+
+ if (rr->key->type != DNS_TYPE_NSEC)
+ continue;
+
+ /* The following checks only make sense for NSEC RRs that are not expanded from a wildcard */
+ r = dns_resource_record_is_synthetic(rr);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ continue;
+
+ /* Check if this is a direct match. If so, we have encountered a NODATA case */
+ r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), name);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ /* If it's not a direct match, maybe it's a wild card match? */
+ r = dnssec_nsec_wildcard_equal(rr, name);
+ if (r < 0)
+ return r;
+ }
+ if (r > 0) {
+ if (key->type == DNS_TYPE_DS) {
+ /* If we look for a DS RR and the server sent us the NSEC RR of the child zone
+ * we have a problem. For DS RRs we want the NSEC RR from the parent */
+ if (bitmap_isset(rr->nsec.types, DNS_TYPE_SOA))
+ continue;
+ } else {
+ /* For all RR types, ensure that if NS is set SOA is set too, so that we know
+ * we got the child's NSEC. */
+ if (bitmap_isset(rr->nsec.types, DNS_TYPE_NS) &&
+ !bitmap_isset(rr->nsec.types, DNS_TYPE_SOA))
+ continue;
+ }
+
+ if (bitmap_isset(rr->nsec.types, key->type))
+ *result = DNSSEC_NSEC_FOUND;
+ else if (bitmap_isset(rr->nsec.types, DNS_TYPE_CNAME))
+ *result = DNSSEC_NSEC_CNAME;
+ else
+ *result = DNSSEC_NSEC_NODATA;
+
+ if (authenticated)
+ *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
+ if (ttl)
+ *ttl = rr->ttl;
+
+ return 0;
+ }
+
+ /* Check if the name we are looking for is an empty non-terminal within the owner or next name
+ * of the NSEC RR. */
+ r = dnssec_nsec_in_path(rr, name);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ *result = DNSSEC_NSEC_NODATA;
+
+ if (authenticated)
+ *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
+ if (ttl)
+ *ttl = rr->ttl;
+
+ return 0;
+ }
+
+ /* The following two "covering" checks, are not useful if the NSEC is from the parent */
+ r = dnssec_nsec_from_parent_zone(rr, name);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ continue;
+
+ /* Check if this NSEC RR proves the absence of an explicit RR under this name */
+ r = dnssec_nsec_covers(rr, name);
+ if (r < 0)
+ return r;
+ if (r > 0 && (!covering_rr || !covering_rr_authenticated)) {
+ covering_rr = rr;
+ covering_rr_authenticated = flags & DNS_ANSWER_AUTHENTICATED;
+ }
+
+ /* Check if this NSEC RR proves the absence of a wildcard RR under this name */
+ r = dnssec_nsec_covers_wildcard(rr, name);
+ if (r < 0)
+ return r;
+ if (r > 0 && (!wildcard_rr || !wildcard_rr_authenticated)) {
+ wildcard_rr = rr;
+ wildcard_rr_authenticated = flags & DNS_ANSWER_AUTHENTICATED;
+ }
+ }
+
+ if (covering_rr && wildcard_rr) {
+ /* If we could prove that neither the name itself, nor the wildcard at the closest encloser exists, we
+ * proved the NXDOMAIN case. */
+ *result = DNSSEC_NSEC_NXDOMAIN;
+
+ if (authenticated)
+ *authenticated = covering_rr_authenticated && wildcard_rr_authenticated;
+ if (ttl)
+ *ttl = MIN(covering_rr->ttl, wildcard_rr->ttl);
+
+ return 0;
+ }
+
+ /* OK, this was not sufficient. Let's see if NSEC3 can help. */
+ if (have_nsec3)
+ return dnssec_test_nsec3(answer, key, result, authenticated, ttl);
+
+ /* No approproate NSEC RR found, report this. */
+ *result = DNSSEC_NSEC_NO_RR;
+ return 0;
+}
+
+int dnssec_nsec_test_enclosed(DnsAnswer *answer, uint16_t type, const char *name, const char *zone, bool *authenticated) {
+ DnsResourceRecord *rr;
+ DnsAnswerFlags flags;
+ int r;
+
+ assert(name);
+ assert(zone);
+
+ /* Checks whether there's an NSEC/NSEC3 that proves that the specified 'name' is non-existing in the specified
+ * 'zone'. The 'zone' must be a suffix of the 'name'. */
+
+ DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
+ bool found = false;
+
+ if (rr->key->type != type && type != DNS_TYPE_ANY)
+ continue;
+
+ switch (rr->key->type) {
+
+ case DNS_TYPE_NSEC:
+
+ /* We only care for NSEC RRs from the indicated zone */
+ r = dns_resource_record_is_signer(rr, zone);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), name, rr->nsec.next_domain_name);
+ if (r < 0)
+ return r;
+
+ found = r > 0;
+ break;
+
+ case DNS_TYPE_NSEC3: {
+ _cleanup_free_ char *hashed_domain = NULL, *next_hashed_domain = NULL;
+
+ /* We only care for NSEC3 RRs from the indicated zone */
+ r = dns_resource_record_is_signer(rr, zone);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ r = nsec3_is_good(rr, NULL);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ /* Format the domain we are testing with the NSEC3 RR's hash function */
+ r = nsec3_hashed_domain_make(
+ rr,
+ name,
+ zone,
+ &hashed_domain);
+ if (r < 0)
+ return r;
+ if ((size_t) r != rr->nsec3.next_hashed_name_size)
+ break;
+
+ /* Format the NSEC3's next hashed name as proper domain name */
+ r = nsec3_hashed_domain_format(
+ rr->nsec3.next_hashed_name,
+ rr->nsec3.next_hashed_name_size,
+ zone,
+ &next_hashed_domain);
+ if (r < 0)
+ return r;
+
+ r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), hashed_domain, next_hashed_domain);
+ if (r < 0)
+ return r;
+
+ found = r > 0;
+ break;
+ }
+
+ default:
+ continue;
+ }
+
+ if (found) {
+ if (authenticated)
+ *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int dnssec_test_positive_wildcard_nsec3(
+ DnsAnswer *answer,
+ const char *name,
+ const char *source,
+ const char *zone,
+ bool *authenticated) {
+
+ const char *next_closer = NULL;
+ int r;
+
+ /* Run a positive NSEC3 wildcard proof. Specifically:
+ *
+ * A proof that the the "next closer" of the generating wildcard does not exist.
+ *
+ * Note a key difference between the NSEC3 and NSEC versions of the proof. NSEC RRs don't have to exist for
+ * empty non-transients. NSEC3 RRs however have to. This means it's sufficient to check if the next closer name
+ * exists for the NSEC3 RR and we are done.
+ *
+ * To prove that a.b.c.d.e.f is rightfully synthesized from a wildcard *.d.e.f all we have to check is that
+ * c.d.e.f does not exist. */
+
+ for (;;) {
+ next_closer = name;
+ r = dns_name_parent(&name);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 0;
+
+ r = dns_name_equal(name, source);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ break;
+ }
+
+ return dnssec_nsec_test_enclosed(answer, DNS_TYPE_NSEC3, next_closer, zone, authenticated);
+}
+
+static int dnssec_test_positive_wildcard_nsec(
+ DnsAnswer *answer,
+ const char *name,
+ const char *source,
+ const char *zone,
+ bool *_authenticated) {
+
+ bool authenticated = true;
+ int r;
+
+ /* Run a positive NSEC wildcard proof. Specifically:
+ *
+ * A proof that there's neither a wildcard name nor a non-wildcard name that is a suffix of the name "name" and
+ * a prefix of the synthesizing source "source" in the zone "zone".
+ *
+ * See RFC 5155, Section 8.8 and RFC 4035, Section 5.3.4
+ *
+ * Note that if we want to prove that a.b.c.d.e.f is rightfully synthesized from a wildcard *.d.e.f, then we
+ * have to prove that none of the following exist:
+ *
+ * 1) a.b.c.d.e.f
+ * 2) *.b.c.d.e.f
+ * 3) b.c.d.e.f
+ * 4) *.c.d.e.f
+ * 5) c.d.e.f
+ *
+ */
+
+ for (;;) {
+ _cleanup_free_ char *wc = NULL;
+ bool a = false;
+
+ /* Check if there's an NSEC or NSEC3 RR that proves that the mame we determined is really non-existing,
+ * i.e between the owner name and the next name of an NSEC RR. */
+ r = dnssec_nsec_test_enclosed(answer, DNS_TYPE_NSEC, name, zone, &a);
+ if (r <= 0)
+ return r;
+
+ authenticated = authenticated && a;
+
+ /* Strip one label off */
+ r = dns_name_parent(&name);
+ if (r <= 0)
+ return r;
+
+ /* Did we reach the source of synthesis? */
+ r = dns_name_equal(name, source);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ /* Successful exit */
+ *_authenticated = authenticated;
+ return 1;
+ }
+
+ /* Safety check, that the source of synthesis is still our suffix */
+ r = dns_name_endswith(name, source);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EBADMSG;
+
+ /* Replace the label we stripped off with an asterisk */
+ wc = strappend("*.", name);
+ if (!wc)
+ return -ENOMEM;
+
+ /* And check if the proof holds for the asterisk name, too */
+ r = dnssec_nsec_test_enclosed(answer, DNS_TYPE_NSEC, wc, zone, &a);
+ if (r <= 0)
+ return r;
+
+ authenticated = authenticated && a;
+ /* In the next iteration we'll check the non-asterisk-prefixed version */
+ }
+}
+
+int dnssec_test_positive_wildcard(
+ DnsAnswer *answer,
+ const char *name,
+ const char *source,
+ const char *zone,
+ bool *authenticated) {
+
+ int r;
+
+ assert(name);
+ assert(source);
+ assert(zone);
+ assert(authenticated);
+
+ r = dns_answer_contains_zone_nsec3(answer, zone);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return dnssec_test_positive_wildcard_nsec3(answer, name, source, zone, authenticated);
+ else
+ return dnssec_test_positive_wildcard_nsec(answer, name, source, zone, authenticated);
+}
+
+static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = {
+ [DNSSEC_VALIDATED] = "validated",
+ [DNSSEC_VALIDATED_WILDCARD] = "validated-wildcard",
+ [DNSSEC_INVALID] = "invalid",
+ [DNSSEC_SIGNATURE_EXPIRED] = "signature-expired",
+ [DNSSEC_UNSUPPORTED_ALGORITHM] = "unsupported-algorithm",
+ [DNSSEC_NO_SIGNATURE] = "no-signature",
+ [DNSSEC_MISSING_KEY] = "missing-key",
+ [DNSSEC_UNSIGNED] = "unsigned",
+ [DNSSEC_FAILED_AUXILIARY] = "failed-auxiliary",
+ [DNSSEC_NSEC_MISMATCH] = "nsec-mismatch",
+ [DNSSEC_INCOMPATIBLE_SERVER] = "incompatible-server",
+};
+DEFINE_STRING_TABLE_LOOKUP(dnssec_result, DnssecResult);
diff --git a/src/resolve/resolved-dns-dnssec.h b/src/resolve/resolved-dns-dnssec.h
new file mode 100644
index 0000000000..955017e8cb
--- /dev/null
+++ b/src/resolve/resolved-dns-dnssec.h
@@ -0,0 +1,92 @@
+/*-*- 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/>.
+***/
+
+typedef enum DnssecMode DnssecMode;
+typedef enum DnssecResult DnssecResult;
+
+#include "dns-domain.h"
+#include "resolved-dns-answer.h"
+#include "resolved-dns-rr.h"
+
+enum DnssecResult {
+ /* These five are returned by dnssec_verify_rrset() */
+ DNSSEC_VALIDATED,
+ DNSSEC_VALIDATED_WILDCARD, /* Validated via a wildcard RRSIG, further NSEC/NSEC3 checks necessary */
+ DNSSEC_INVALID,
+ DNSSEC_SIGNATURE_EXPIRED,
+ DNSSEC_UNSUPPORTED_ALGORITHM,
+
+ /* These two are added by dnssec_verify_rrset_search() */
+ DNSSEC_NO_SIGNATURE,
+ DNSSEC_MISSING_KEY,
+
+ /* These two are added by the DnsTransaction logic */
+ DNSSEC_UNSIGNED,
+ DNSSEC_FAILED_AUXILIARY,
+ DNSSEC_NSEC_MISMATCH,
+ DNSSEC_INCOMPATIBLE_SERVER,
+
+ _DNSSEC_RESULT_MAX,
+ _DNSSEC_RESULT_INVALID = -1
+};
+
+#define DNSSEC_CANONICAL_HOSTNAME_MAX (DNS_HOSTNAME_MAX + 2)
+
+/* The longest digest we'll ever generate, of all digest algorithms we support */
+#define DNSSEC_HASH_SIZE_MAX (MAX(20, 32))
+
+int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, bool revoked_ok);
+int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig);
+
+int dnssec_verify_rrset(DnsAnswer *answer, const DnsResourceKey *key, DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, usec_t realtime, DnssecResult *result);
+int dnssec_verify_rrset_search(DnsAnswer *answer, const DnsResourceKey *key, DnsAnswer *validated_dnskeys, usec_t realtime, DnssecResult *result, DnsResourceRecord **rrsig);
+
+int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke);
+int dnssec_verify_dnskey_by_ds_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds);
+
+int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key);
+
+uint16_t dnssec_keytag(DnsResourceRecord *dnskey, bool mask_revoke);
+
+int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max);
+
+int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret);
+
+typedef enum DnssecNsecResult {
+ DNSSEC_NSEC_NO_RR, /* No suitable NSEC/NSEC3 RR found */
+ DNSSEC_NSEC_CNAME, /* Didn't find what was asked for, but did find CNAME */
+ DNSSEC_NSEC_UNSUPPORTED_ALGORITHM,
+ DNSSEC_NSEC_NXDOMAIN,
+ DNSSEC_NSEC_NODATA,
+ DNSSEC_NSEC_FOUND,
+ DNSSEC_NSEC_OPTOUT,
+} DnssecNsecResult;
+
+int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl);
+
+int dnssec_nsec_test_enclosed(DnsAnswer *answer, uint16_t type, const char *name, const char *zone, bool *authenticated);
+
+int dnssec_test_positive_wildcard(DnsAnswer *a, const char *name, const char *source, const char *zone, bool *authenticated);
+
+const char* dnssec_result_to_string(DnssecResult m) _const_;
+DnssecResult dnssec_result_from_string(const char *s) _pure_;
diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c
index bebd1ee4a6..9a5223ef01 100644
--- a/src/resolve/resolved-dns-packet.c
+++ b/src/resolve/resolved-dns-packet.c
@@ -19,12 +19,16 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "utf8.h"
-#include "util.h"
-#include "strv.h"
-#include "unaligned.h"
+#include "alloc-util.h"
#include "dns-domain.h"
#include "resolved-dns-packet.h"
+#include "string-table.h"
+#include "strv.h"
+#include "unaligned.h"
+#include "utf8.h"
+#include "util.h"
+
+#define EDNS0_OPT_DO (1<<15)
int dns_packet_new(DnsPacket **ret, DnsProtocol protocol, size_t mtu) {
DnsPacket *p;
@@ -54,6 +58,7 @@ int dns_packet_new(DnsPacket **ret, DnsProtocol protocol, size_t mtu) {
p->size = p->rindex = DNS_PACKET_HEADER_SIZE;
p->allocated = a;
p->protocol = protocol;
+ p->opt_start = p->opt_size = (size_t) -1;
p->n_ref = 1;
*ret = p;
@@ -61,20 +66,18 @@ int dns_packet_new(DnsPacket **ret, DnsProtocol protocol, size_t mtu) {
return 0;
}
-int dns_packet_new_query(DnsPacket **ret, DnsProtocol protocol, size_t mtu) {
- DnsPacket *p;
- DnsPacketHeader *h;
- int r;
+void dns_packet_set_flags(DnsPacket *p, bool dnssec_checking_disabled, bool truncated) {
- assert(ret);
+ DnsPacketHeader *h;
- r = dns_packet_new(&p, protocol, mtu);
- if (r < 0)
- return r;
+ assert(p);
h = DNS_PACKET_HEADER(p);
- if (protocol == DNS_PROTOCOL_LLMNR)
+ switch(p->protocol) {
+ case DNS_PROTOCOL_LLMNR:
+ assert(!truncated);
+
h->flags = htobe16(DNS_PACKET_MAKE_FLAGS(0 /* qr */,
0 /* opcode */,
0 /* c */,
@@ -84,7 +87,23 @@ int dns_packet_new_query(DnsPacket **ret, DnsProtocol protocol, size_t mtu) {
0 /* ad */,
0 /* cd */,
0 /* rcode */));
- else
+ break;
+
+ case DNS_PROTOCOL_MDNS:
+ h->flags = htobe16(DNS_PACKET_MAKE_FLAGS(0 /* qr */,
+ 0 /* opcode */,
+ 0 /* aa */,
+ truncated /* tc */,
+ 0 /* rd (ask for recursion) */,
+ 0 /* ra */,
+ 0 /* ad */,
+ 0 /* cd */,
+ 0 /* rcode */));
+ break;
+
+ default:
+ assert(!truncated);
+
h->flags = htobe16(DNS_PACKET_MAKE_FLAGS(0 /* qr */,
0 /* opcode */,
0 /* aa */,
@@ -92,8 +111,25 @@ int dns_packet_new_query(DnsPacket **ret, DnsProtocol protocol, size_t mtu) {
1 /* rd (ask for recursion) */,
0 /* ra */,
0 /* ad */,
- 0 /* cd */,
+ dnssec_checking_disabled /* cd */,
0 /* rcode */));
+ }
+}
+
+int dns_packet_new_query(DnsPacket **ret, DnsProtocol protocol, size_t mtu, bool dnssec_checking_disabled) {
+ DnsPacket *p;
+ int r;
+
+ assert(ret);
+
+ r = dns_packet_new(&p, protocol, mtu);
+ if (r < 0)
+ return r;
+
+ /* Always set the TC bit to 0 initially.
+ * If there are multiple packets later, we'll update the bit shortly before sending.
+ */
+ dns_packet_set_flags(p, dnssec_checking_disabled, false);
*ret = p;
return 0;
@@ -104,6 +140,8 @@ DnsPacket *dns_packet_ref(DnsPacket *p) {
if (!p)
return NULL;
+ assert(!p->on_stack);
+
assert(p->n_ref > 0);
p->n_ref++;
return p;
@@ -116,13 +154,16 @@ static void dns_packet_free(DnsPacket *p) {
dns_question_unref(p->question);
dns_answer_unref(p->answer);
+ dns_resource_record_unref(p->opt);
while ((s = hashmap_steal_first_key(p->names)))
free(s);
hashmap_free(p->names);
free(p->_data);
- free(p);
+
+ if (!p->on_stack)
+ free(p);
}
DnsPacket *dns_packet_unref(DnsPacket *p) {
@@ -131,6 +172,8 @@ DnsPacket *dns_packet_unref(DnsPacket *p) {
assert(p->n_ref > 0);
+ dns_packet_unref(p->more);
+
if (p->n_ref == 1)
dns_packet_free(p);
else
@@ -167,6 +210,7 @@ int dns_packet_validate_reply(DnsPacket *p) {
return -EBADMSG;
switch (p->protocol) {
+
case DNS_PROTOCOL_LLMNR:
/* RFC 4795, Section 2.1.1. says to discard all replies with QDCOUNT != 1 */
if (DNS_PACKET_QDCOUNT(p) != 1)
@@ -174,6 +218,13 @@ int dns_packet_validate_reply(DnsPacket *p) {
break;
+ case DNS_PROTOCOL_MDNS:
+ /* RFC 6762, Section 18 */
+ if (DNS_PACKET_RCODE(p) != 0)
+ return -EBADMSG;
+
+ break;
+
default:
break;
}
@@ -200,6 +251,7 @@ int dns_packet_validate_query(DnsPacket *p) {
return -EBADMSG;
switch (p->protocol) {
+
case DNS_PROTOCOL_LLMNR:
/* RFC 4795, Section 2.1.1. says to discard all queries with QDCOUNT != 1 */
if (DNS_PACKET_QDCOUNT(p) != 1)
@@ -215,6 +267,18 @@ int dns_packet_validate_query(DnsPacket *p) {
break;
+ case DNS_PROTOCOL_MDNS:
+ /* RFC 6762, Section 18 */
+ if (DNS_PACKET_AA(p) != 0 ||
+ DNS_PACKET_RD(p) != 0 ||
+ DNS_PACKET_RA(p) != 0 ||
+ DNS_PACKET_AD(p) != 0 ||
+ DNS_PACKET_CD(p) != 0 ||
+ DNS_PACKET_RCODE(p) != 0)
+ return -EBADMSG;
+
+ break;
+
default:
break;
}
@@ -265,7 +329,7 @@ static int dns_packet_extend(DnsPacket *p, size_t add, void **ret, size_t *start
return 0;
}
-static void dns_packet_truncate(DnsPacket *p, size_t sz) {
+void dns_packet_truncate(DnsPacket *p, size_t sz) {
Iterator i;
char *s;
void *n;
@@ -347,43 +411,68 @@ int dns_packet_append_uint32(DnsPacket *p, uint32_t v, size_t *start) {
}
int dns_packet_append_string(DnsPacket *p, const char *s, size_t *start) {
+ assert(p);
+ assert(s);
+
+ return dns_packet_append_raw_string(p, s, strlen(s), start);
+}
+
+int dns_packet_append_raw_string(DnsPacket *p, const void *s, size_t size, size_t *start) {
void *d;
- size_t l;
int r;
assert(p);
- assert(s);
+ assert(s || size == 0);
- l = strlen(s);
- if (l > 255)
+ if (size > 255)
return -E2BIG;
- r = dns_packet_extend(p, 1 + l, &d, start);
+ r = dns_packet_extend(p, 1 + size, &d, start);
if (r < 0)
return r;
- ((uint8_t*) d)[0] = (uint8_t) l;
- memcpy(((uint8_t*) d) + 1, s, l);
+ ((uint8_t*) d)[0] = (uint8_t) size;
+
+ if (size > 0)
+ memcpy(((uint8_t*) d) + 1, s, size);
return 0;
}
-int dns_packet_append_label(DnsPacket *p, const char *d, size_t l, size_t *start) {
- void *w;
+int dns_packet_append_label(DnsPacket *p, const char *d, size_t l, bool canonical_candidate, size_t *start) {
+ uint8_t *w;
int r;
+ /* Append a label to a packet. Optionally, does this in DNSSEC
+ * canonical form, if this label is marked as a candidate for
+ * it, and the canonical form logic is enabled for the
+ * packet */
+
assert(p);
assert(d);
if (l > DNS_LABEL_MAX)
return -E2BIG;
- r = dns_packet_extend(p, 1 + l, &w, start);
+ r = dns_packet_extend(p, 1 + l, (void**) &w, start);
if (r < 0)
return r;
- ((uint8_t*) w)[0] = (uint8_t) l;
- memcpy(((uint8_t*) w) + 1, d, l);
+ *(w++) = (uint8_t) l;
+
+ if (p->canonical_form && canonical_candidate) {
+ size_t i;
+
+ /* Generate in canonical form, as defined by DNSSEC
+ * RFC 4034, Section 6.2, i.e. all lower-case. */
+
+ for (i = 0; i < l; i++)
+ w[i] = (uint8_t) ascii_tolower(d[i]);
+ } else
+ /* Otherwise, just copy the string unaltered. This is
+ * essential for DNS-SD, where the casing of labels
+ * matters and needs to be retained. */
+ memcpy(w, d, l);
return 0;
}
@@ -392,6 +481,7 @@ int dns_packet_append_name(
DnsPacket *p,
const char *name,
bool allow_compression,
+ bool canonical_candidate,
size_t *start) {
size_t saved_size;
@@ -405,11 +495,10 @@ int dns_packet_append_name(
saved_size = p->size;
- while (*name) {
- _cleanup_free_ char *s = NULL;
+ while (!dns_name_is_root(name)) {
+ const char *z = name;
char label[DNS_LABEL_MAX];
size_t n = 0;
- int k;
if (allow_compression)
n = PTR_TO_SIZE(hashmap_get(p->names, name));
@@ -425,32 +514,23 @@ int dns_packet_append_name(
}
}
- s = strdup(name);
- if (!s) {
- r = -ENOMEM;
- goto fail;
- }
-
r = dns_label_unescape(&name, label, sizeof(label));
if (r < 0)
goto fail;
- if (p->protocol == DNS_PROTOCOL_DNS)
- k = dns_label_apply_idna(label, r, label, sizeof(label));
- else
- k = dns_label_undo_idna(label, r, label, sizeof(label));
- if (k < 0) {
- r = k;
- goto fail;
- }
- if (k > 0)
- r = k;
-
- r = dns_packet_append_label(p, label, r, &n);
+ r = dns_packet_append_label(p, label, r, canonical_candidate, &n);
if (r < 0)
goto fail;
if (allow_compression) {
+ _cleanup_free_ char *s = NULL;
+
+ s = strdup(z);
+ if (!s) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
r = hashmap_ensure_allocated(&p->names, &dns_name_hash_ops);
if (r < 0)
goto fail;
@@ -487,7 +567,7 @@ int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *k, size_t *start)
saved_size = p->size;
- r = dns_packet_append_name(p, DNS_RESOURCE_KEY_NAME(k), true, NULL);
+ r = dns_packet_append_name(p, DNS_RESOURCE_KEY_NAME(k), true, true, NULL);
if (r < 0)
goto fail;
@@ -509,7 +589,7 @@ fail:
return r;
}
-static int dns_packet_append_type_window(DnsPacket *p, uint8_t window, uint8_t length, uint8_t *types, size_t *start) {
+static int dns_packet_append_type_window(DnsPacket *p, uint8_t window, uint8_t length, const uint8_t *types, size_t *start) {
size_t saved_size;
int r;
@@ -550,7 +630,6 @@ static int dns_packet_append_types(DnsPacket *p, Bitmap *types, size_t *start) {
int r;
assert(p);
- assert(types);
saved_size = p->size;
@@ -566,27 +645,143 @@ static int dns_packet_append_types(DnsPacket *p, Bitmap *types, size_t *start) {
}
window = n >> 8;
-
entry = n & 255;
bitmaps[entry / 8] |= 1 << (7 - (entry % 8));
}
- r = dns_packet_append_type_window(p, window, entry / 8 + 1, bitmaps, NULL);
+ if (bitmaps[entry / 8] != 0) {
+ r = dns_packet_append_type_window(p, window, entry / 8 + 1, bitmaps, NULL);
+ if (r < 0)
+ goto fail;
+ }
+
+ if (start)
+ *start = saved_size;
+
+ return 0;
+fail:
+ dns_packet_truncate(p, saved_size);
+ return r;
+}
+
+/* Append the OPT pseudo-RR described in RFC6891 */
+int dns_packet_append_opt(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, size_t *start) {
+ size_t saved_size;
+ int r;
+
+ assert(p);
+ /* we must never advertise supported packet size smaller than the legacy max */
+ assert(max_udp_size >= DNS_PACKET_UNICAST_SIZE_MAX);
+
+ if (p->opt_start != (size_t) -1)
+ return -EBUSY;
+
+ assert(p->opt_size == (size_t) -1);
+
+ saved_size = p->size;
+
+ /* empty name */
+ r = dns_packet_append_uint8(p, 0, NULL);
+ if (r < 0)
+ return r;
+
+ /* type */
+ r = dns_packet_append_uint16(p, DNS_TYPE_OPT, NULL);
+ if (r < 0)
+ goto fail;
+
+ /* maximum udp packet that can be received */
+ r = dns_packet_append_uint16(p, max_udp_size, NULL);
if (r < 0)
goto fail;
+ /* extended RCODE and VERSION */
+ r = dns_packet_append_uint16(p, 0, NULL);
+ if (r < 0)
+ goto fail;
+
+ /* flags: DNSSEC OK (DO), see RFC3225 */
+ r = dns_packet_append_uint16(p, edns0_do ? EDNS0_OPT_DO : 0, NULL);
+ if (r < 0)
+ goto fail;
+
+ /* RDLENGTH */
+
+ if (edns0_do) {
+ /* If DO is on, also append RFC6975 Algorithm data */
+
+ static const uint8_t rfc6975[] = {
+
+ 0, 5, /* OPTION_CODE: DAU */
+ 0, 6, /* LIST_LENGTH */
+ DNSSEC_ALGORITHM_RSASHA1,
+ DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1,
+ DNSSEC_ALGORITHM_RSASHA256,
+ DNSSEC_ALGORITHM_RSASHA512,
+ DNSSEC_ALGORITHM_ECDSAP256SHA256,
+ DNSSEC_ALGORITHM_ECDSAP384SHA384,
+
+ 0, 6, /* OPTION_CODE: DHU */
+ 0, 3, /* LIST_LENGTH */
+ DNSSEC_DIGEST_SHA1,
+ DNSSEC_DIGEST_SHA256,
+ DNSSEC_DIGEST_SHA384,
+
+ 0, 7, /* OPTION_CODE: N3U */
+ 0, 1, /* LIST_LENGTH */
+ NSEC3_ALGORITHM_SHA1,
+ };
+
+ r = dns_packet_append_uint16(p, sizeof(rfc6975), NULL);
+ if (r < 0)
+ goto fail;
+
+ r = dns_packet_append_blob(p, rfc6975, sizeof(rfc6975), NULL);
+ } else
+ r = dns_packet_append_uint16(p, 0, NULL);
+
+ if (r < 0)
+ goto fail;
+
+ DNS_PACKET_HEADER(p)->arcount = htobe16(DNS_PACKET_ARCOUNT(p) + 1);
+
+ p->opt_start = saved_size;
+ p->opt_size = p->size - saved_size;
+
if (start)
*start = saved_size;
return 0;
+
fail:
dns_packet_truncate(p, saved_size);
return r;
}
-int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *start) {
- size_t saved_size, rdlength_offset, end, rdlength;
+int dns_packet_truncate_opt(DnsPacket *p) {
+ assert(p);
+
+ if (p->opt_start == (size_t) -1) {
+ assert(p->opt_size == (size_t) -1);
+ return 0;
+ }
+
+ assert(p->opt_size != (size_t) -1);
+ assert(DNS_PACKET_ARCOUNT(p) > 0);
+
+ if (p->opt_start + p->opt_size != p->size)
+ return -EBUSY;
+
+ dns_packet_truncate(p, p->opt_start);
+ DNS_PACKET_HEADER(p)->arcount = htobe16(DNS_PACKET_ARCOUNT(p) - 1);
+ p->opt_start = p->opt_size = (size_t) -1;
+
+ return 1;
+}
+
+int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *start, size_t *rdata_start) {
+ size_t saved_size, rdlength_offset, end, rdlength, rds;
int r;
assert(p);
@@ -607,6 +802,8 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
if (r < 0)
goto fail;
+ rds = p->size - saved_size;
+
switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
case DNS_TYPE_SRV:
@@ -622,14 +819,14 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
if (r < 0)
goto fail;
- r = dns_packet_append_name(p, rr->srv.name, true, NULL);
+ r = dns_packet_append_name(p, rr->srv.name, true, false, NULL);
break;
case DNS_TYPE_PTR:
case DNS_TYPE_NS:
case DNS_TYPE_CNAME:
case DNS_TYPE_DNAME:
- r = dns_packet_append_name(p, rr->ptr.name, true, NULL);
+ r = dns_packet_append_name(p, rr->ptr.name, true, false, NULL);
break;
case DNS_TYPE_HINFO:
@@ -641,19 +838,20 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
break;
case DNS_TYPE_SPF: /* exactly the same as TXT */
- case DNS_TYPE_TXT: {
- char **s;
+ case DNS_TYPE_TXT:
- if (strv_isempty(rr->txt.strings)) {
+ if (!rr->txt.items) {
/* RFC 6763, section 6.1 suggests to generate
* single empty string for an empty array. */
- r = dns_packet_append_string(p, "", NULL);
+ r = dns_packet_append_raw_string(p, NULL, 0, NULL);
if (r < 0)
goto fail;
} else {
- STRV_FOREACH(s, rr->txt.strings) {
- r = dns_packet_append_string(p, *s, NULL);
+ DnsTxtItem *i;
+
+ LIST_FOREACH(items, i, rr->txt.items) {
+ r = dns_packet_append_raw_string(p, i->data, i->length, NULL);
if (r < 0)
goto fail;
}
@@ -661,7 +859,6 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
r = 0;
break;
- }
case DNS_TYPE_A:
r = dns_packet_append_blob(p, &rr->a.in_addr, sizeof(struct in_addr), NULL);
@@ -672,11 +869,11 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
break;
case DNS_TYPE_SOA:
- r = dns_packet_append_name(p, rr->soa.mname, true, NULL);
+ r = dns_packet_append_name(p, rr->soa.mname, true, false, NULL);
if (r < 0)
goto fail;
- r = dns_packet_append_name(p, rr->soa.rname, true, NULL);
+ r = dns_packet_append_name(p, rr->soa.rname, true, false, NULL);
if (r < 0)
goto fail;
@@ -704,7 +901,7 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
if (r < 0)
goto fail;
- r = dns_packet_append_name(p, rr->mx.exchange, true, NULL);
+ r = dns_packet_append_name(p, rr->mx.exchange, true, false, NULL);
break;
case DNS_TYPE_LOC:
@@ -764,11 +961,11 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
break;
case DNS_TYPE_DNSKEY:
- r = dns_packet_append_uint16(p, dnskey_to_flags(rr), NULL);
+ r = dns_packet_append_uint16(p, rr->dnskey.flags, NULL);
if (r < 0)
goto fail;
- r = dns_packet_append_uint8(p, 3u, NULL);
+ r = dns_packet_append_uint8(p, rr->dnskey.protocol, NULL);
if (r < 0)
goto fail;
@@ -808,7 +1005,7 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
if (r < 0)
goto fail;
- r = dns_packet_append_name(p, rr->rrsig.signer, false, NULL);
+ r = dns_packet_append_name(p, rr->rrsig.signer, false, true, NULL);
if (r < 0)
goto fail;
@@ -816,7 +1013,7 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
break;
case DNS_TYPE_NSEC:
- r = dns_packet_append_name(p, rr->nsec.next_domain_name, false, NULL);
+ r = dns_packet_append_name(p, rr->nsec.next_domain_name, false, false, NULL);
if (r < 0)
goto fail;
@@ -825,6 +1022,7 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
goto fail;
break;
+
case DNS_TYPE_NSEC3:
r = dns_packet_append_uint8(p, rr->nsec3.algorithm, NULL);
if (r < 0)
@@ -859,6 +1057,8 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
goto fail;
break;
+
+ case DNS_TYPE_OPT:
case _DNS_TYPE_INVALID: /* unparseable */
default:
@@ -871,7 +1071,7 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
/* Let's calculate the actual data size and update the field */
rdlength = p->size - rdlength_offset - sizeof(uint16_t);
if (rdlength > 0xFFFF) {
- r = ENOSPC;
+ r = -ENOSPC;
goto fail;
}
@@ -885,6 +1085,9 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
if (start)
*start = saved_size;
+ if (rdata_start)
+ *rdata_start = rds;
+
return 0;
fail:
@@ -892,7 +1095,6 @@ fail:
return r;
}
-
int dns_packet_read(DnsPacket *p, size_t sz, const void **ret, size_t *start) {
assert(p);
@@ -1060,6 +1262,35 @@ fail:
return r;
}
+int dns_packet_read_raw_string(DnsPacket *p, const void **ret, size_t *size, size_t *start) {
+ size_t saved_rindex;
+ uint8_t c;
+ int r;
+
+ assert(p);
+
+ saved_rindex = p->rindex;
+
+ r = dns_packet_read_uint8(p, &c, NULL);
+ if (r < 0)
+ goto fail;
+
+ r = dns_packet_read(p, c, ret, NULL);
+ if (r < 0)
+ goto fail;
+
+ if (size)
+ *size = c;
+ if (start)
+ *start = saved_rindex;
+
+ return 0;
+
+fail:
+ dns_packet_rewind(p, saved_rindex);
+ return r;
+}
+
int dns_packet_read_name(
DnsPacket *p,
char **_ret,
@@ -1092,7 +1323,6 @@ int dns_packet_read_name(
/* End of name */
break;
else if (c <= 63) {
- _cleanup_free_ char *t = NULL;
const char *label;
/* Literal label */
@@ -1100,21 +1330,20 @@ int dns_packet_read_name(
if (r < 0)
goto fail;
- r = dns_label_escape(label, c, &t);
- if (r < 0)
- goto fail;
-
- if (!GREEDY_REALLOC(ret, allocated, n + !first + strlen(t) + 1)) {
+ if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) {
r = -ENOMEM;
goto fail;
}
- if (!first)
- ret[n++] = '.';
- else
+ if (first)
first = false;
+ else
+ ret[n++] = '.';
+
+ r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX);
+ if (r < 0)
+ goto fail;
- memcpy(ret + n, t, r);
n += r;
continue;
} else if (allow_compression && (c & 0xc0) == 0xc0) {
@@ -1275,8 +1504,9 @@ fail:
return r;
}
-int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start) {
+int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flush, size_t *start) {
_cleanup_free_ char *name = NULL;
+ bool cache_flush = false;
uint16_t class, type;
DnsResourceKey *key;
size_t saved_rindex;
@@ -1299,6 +1529,15 @@ int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start) {
if (r < 0)
goto fail;
+ if (p->protocol == DNS_PROTOCOL_MDNS) {
+ /* See RFC6762, Section 10.2 */
+
+ if (type != DNS_TYPE_OPT && (class & MDNS_RR_CACHE_FLUSH)) {
+ class &= ~MDNS_RR_CACHE_FLUSH;
+ cache_flush = true;
+ }
+ }
+
key = dns_resource_key_new_consume(class, type, name);
if (!key) {
r = -ENOMEM;
@@ -1308,6 +1547,8 @@ int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start) {
name = NULL;
*ret = key;
+ if (ret_cache_flush)
+ *ret_cache_flush = cache_flush;
if (start)
*start = saved_rindex;
@@ -1323,22 +1564,12 @@ static bool loc_size_ok(uint8_t size) {
return m <= 9 && e <= 9 && (m > 0 || e == 0);
}
-static int dnskey_parse_flags(DnsResourceRecord *rr, uint16_t flags) {
- assert(rr);
-
- if (flags & ~(DNSKEY_FLAG_SEP | DNSKEY_FLAG_ZONE_KEY))
- return -EBADMSG;
-
- rr->dnskey.zone_key_flag = flags & DNSKEY_FLAG_ZONE_KEY;
- rr->dnskey.sep_flag = flags & DNSKEY_FLAG_SEP;
- return 0;
-}
-
-int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
+int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_flush, size_t *start) {
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
size_t saved_rindex, offset;
uint16_t rdlength;
+ bool cache_flush;
int r;
assert(p);
@@ -1346,12 +1577,12 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
saved_rindex = p->rindex;
- r = dns_packet_read_key(p, &key, NULL);
+ r = dns_packet_read_key(p, &key, &cache_flush, NULL);
if (r < 0)
goto fail;
- if (key->class == DNS_CLASS_ANY ||
- key->type == DNS_TYPE_ANY) {
+ if (!dns_class_is_valid_rr(key->class)||
+ !dns_type_is_valid_rr(key->type)) {
r = -EBADMSG;
goto fail;
}
@@ -1366,6 +1597,11 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
if (r < 0)
goto fail;
+ /* RFC 2181, Section 8, suggests to
+ * treat a TTL with the MSB set as a zero TTL. */
+ if (rr->ttl & UINT32_C(0x80000000))
+ rr->ttl = 0;
+
r = dns_packet_read_uint16(p, &rdlength, NULL);
if (r < 0)
goto fail;
@@ -1410,24 +1646,37 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
case DNS_TYPE_SPF: /* exactly the same as TXT */
case DNS_TYPE_TXT:
if (rdlength <= 0) {
+ DnsTxtItem *i;
/* RFC 6763, section 6.1 suggests to treat
* empty TXT RRs as equivalent to a TXT record
* with a single empty string. */
- r = strv_extend(&rr->txt.strings, "");
- if (r < 0)
- goto fail;
+ i = malloc0(offsetof(DnsTxtItem, data) + 1); /* for safety reasons we add an extra NUL byte */
+ if (!i)
+ return -ENOMEM;
+
+ rr->txt.items = i;
} else {
+ DnsTxtItem *last = NULL;
+
while (p->rindex < offset + rdlength) {
- char *s;
+ DnsTxtItem *i;
+ const void *data;
+ size_t sz;
- r = dns_packet_read_string(p, &s, NULL);
+ r = dns_packet_read_raw_string(p, &data, &sz, NULL);
if (r < 0)
- goto fail;
+ return r;
- r = strv_consume(&rr->txt.strings, s);
- if (r < 0)
- goto fail;
+ i = malloc0(offsetof(DnsTxtItem, data) + sz + 1); /* extra NUL byte at the end */
+ if (!i)
+ return -ENOMEM;
+
+ memcpy(i->data, data, sz);
+ i->length = sz;
+
+ LIST_INSERT_AFTER(items, rr->txt.items, last, i);
+ last = i;
}
}
@@ -1563,6 +1812,7 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
}
break;
+
case DNS_TYPE_SSHFP:
r = dns_packet_read_uint8(p, &rr->sshfp.algorithm, NULL);
if (r < 0)
@@ -1585,28 +1835,15 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
break;
- case DNS_TYPE_DNSKEY: {
- uint16_t flags;
- uint8_t proto;
-
- r = dns_packet_read_uint16(p, &flags, NULL);
- if (r < 0)
- goto fail;
-
- r = dnskey_parse_flags(rr, flags);
+ case DNS_TYPE_DNSKEY:
+ r = dns_packet_read_uint16(p, &rr->dnskey.flags, NULL);
if (r < 0)
goto fail;
- r = dns_packet_read_uint8(p, &proto, NULL);
+ r = dns_packet_read_uint8(p, &rr->dnskey.protocol, NULL);
if (r < 0)
goto fail;
- /* protocol is required to be always 3 */
- if (proto != 3) {
- r = -EBADMSG;
- goto fail;
- }
-
r = dns_packet_read_uint8(p, &rr->dnskey.algorithm, NULL);
if (r < 0)
goto fail;
@@ -1623,7 +1860,6 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
}
break;
- }
case DNS_TYPE_RRSIG:
r = dns_packet_read_uint16(p, &rr->rrsig.type_covered, NULL);
@@ -1671,8 +1907,16 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
break;
- case DNS_TYPE_NSEC:
- r = dns_packet_read_name(p, &rr->nsec.next_domain_name, false, NULL);
+ case DNS_TYPE_NSEC: {
+
+ /*
+ * RFC6762, section 18.14 explictly states mDNS should use name compression.
+ * This contradicts RFC3845, section 2.1.1
+ */
+
+ bool allow_compressed = p->protocol == DNS_PROTOCOL_MDNS;
+
+ r = dns_packet_read_name(p, &rr->nsec.next_domain_name, allow_compressed, NULL);
if (r < 0)
goto fail;
@@ -1680,15 +1924,12 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
if (r < 0)
goto fail;
- /* The types bitmap must contain at least the NSEC record itself, so an empty bitmap means
- something went wrong */
- if (bitmap_isclear(rr->nsec.types)) {
- r = -EBADMSG;
- goto fail;
- }
+ /* We accept empty NSEC bitmaps. The bit indicating the presence of the NSEC record itself
+ * is redundant and in e.g., RFC4956 this fact is used to define a use for NSEC records
+ * without the NSEC bit set. */
break;
-
+ }
case DNS_TYPE_NSEC3: {
uint8_t size;
@@ -1734,6 +1975,8 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
break;
}
+
+ case DNS_TYPE_OPT: /* we only care about the header of OPT for now. */
default:
unparseable:
r = dns_packet_read_memdup(p, rdlength, &rr->generic.data, &rr->generic.size, NULL);
@@ -1751,6 +1994,8 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
*ret = rr;
rr = NULL;
+ if (ret_cache_flush)
+ *ret_cache_flush = cache_flush;
if (start)
*start = saved_rindex;
@@ -1760,6 +2005,48 @@ fail:
return r;
}
+static bool opt_is_good(DnsResourceRecord *rr, bool *rfc6975) {
+ const uint8_t* p;
+ bool found_dau_dhu_n3u = false;
+ size_t l;
+
+ /* Checks whether the specified OPT RR is well-formed and whether it contains RFC6975 data (which is not OK in
+ * a reply). */
+
+ assert(rr);
+ assert(rr->key->type == DNS_TYPE_OPT);
+
+ /* Check that the version is 0 */
+ if (((rr->ttl >> 16) & UINT32_C(0xFF)) != 0)
+ return false;
+
+ p = rr->opt.data;
+ l = rr->opt.size;
+ while (l > 0) {
+ uint16_t option_code, option_length;
+
+ /* At least four bytes for OPTION-CODE and OPTION-LENGTH are required */
+ if (l < 4U)
+ return false;
+
+ option_code = unaligned_read_be16(p);
+ option_length = unaligned_read_be16(p + 2);
+
+ if (l < option_length + 4U)
+ return false;
+
+ /* RFC 6975 DAU, DHU or N3U fields found. */
+ if (IN_SET(option_code, 5, 6, 7))
+ found_dau_dhu_n3u = true;
+
+ p += option_length + 4U;
+ l -= option_length + 4U;
+ }
+
+ *rfc6975 = found_dau_dhu_n3u;
+ return true;
+}
+
int dns_packet_extract(DnsPacket *p) {
_cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
@@ -1783,11 +2070,22 @@ int dns_packet_extract(DnsPacket *p) {
for (i = 0; i < n; i++) {
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
+ bool cache_flush;
- r = dns_packet_read_key(p, &key, NULL);
+ r = dns_packet_read_key(p, &key, &cache_flush, NULL);
if (r < 0)
goto finish;
+ if (cache_flush) {
+ r = -EBADMSG;
+ goto finish;
+ }
+
+ if (!dns_type_is_valid_query(key->type)) {
+ r = -EBADMSG;
+ goto finish;
+ }
+
r = dns_question_add(question, key);
if (r < 0)
goto finish;
@@ -1796,6 +2094,9 @@ int dns_packet_extract(DnsPacket *p) {
n = DNS_PACKET_RRCOUNT(p);
if (n > 0) {
+ DnsResourceRecord *previous = NULL;
+ bool bad_opt = false;
+
answer = dns_answer_new(n);
if (!answer) {
r = -ENOMEM;
@@ -1804,15 +2105,79 @@ int dns_packet_extract(DnsPacket *p) {
for (i = 0; i < n; i++) {
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+ bool cache_flush;
- r = dns_packet_read_rr(p, &rr, NULL);
+ r = dns_packet_read_rr(p, &rr, &cache_flush, NULL);
if (r < 0)
goto finish;
- r = dns_answer_add(answer, rr, p->ifindex);
- if (r < 0)
- goto finish;
+ /* Try to reduce memory usage a bit */
+ if (previous)
+ dns_resource_key_reduce(&rr->key, &previous->key);
+
+ if (rr->key->type == DNS_TYPE_OPT) {
+ bool has_rfc6975;
+
+ if (p->opt || bad_opt) {
+ /* Multiple OPT RRs? if so, let's ignore all, because there's something wrong
+ * with the server, and if one is valid we wouldn't know which one. */
+ log_debug("Multiple OPT RRs detected, ignoring all.");
+ bad_opt = true;
+ continue;
+ }
+
+ if (!dns_name_is_root(DNS_RESOURCE_KEY_NAME(rr->key))) {
+ /* If the OPT RR qis not owned by the root domain, then it is bad, let's ignore
+ * it. */
+ log_debug("OPT RR is not owned by root domain, ignoring.");
+ bad_opt = true;
+ continue;
+ }
+
+ if (i < DNS_PACKET_ANCOUNT(p) + DNS_PACKET_NSCOUNT(p)) {
+ /* OPT RR is in the wrong section? Some Belkin routers do this. This is a hint
+ * the EDNS implementation is borked, like the Belkin one is, hence ignore
+ * it. */
+ log_debug("OPT RR in wrong section, ignoring.");
+ bad_opt = true;
+ continue;
+ }
+
+ if (!opt_is_good(rr, &has_rfc6975)) {
+ log_debug("Malformed OPT RR, ignoring.");
+ bad_opt = true;
+ continue;
+ }
+
+ if (has_rfc6975) {
+ /* OPT RR contains RFC6975 algorithm data, then this is indication that the
+ * server just copied the OPT it got from us (which contained that data) back
+ * into the reply. If so, then it doesn't properly support EDNS, as RFC6975
+ * makes it very clear that the algorithm data should only be contained in
+ * questions, never in replies. Crappy Belkin copy the OPT data for example,
+ * hence let's detect this so that we downgrade early. */
+ log_debug("OPT RR contained RFC6975 data, ignoring.");
+ bad_opt = true;
+ continue;
+ }
+
+ p->opt = dns_resource_record_ref(rr);
+ } else {
+
+ /* According to RFC 4795, section 2.9. only the RRs from the Answer section shall be
+ * cached. Hence mark only those RRs as cacheable by default, but not the ones from the
+ * Additional or Authority sections. */
+
+ r = dns_answer_add(answer, rr, p->ifindex,
+ (i < DNS_PACKET_ANCOUNT(p) ? DNS_ANSWER_CACHEABLE : 0) |
+ (p->protocol == DNS_PROTOCOL_MDNS && !cache_flush ? DNS_ANSWER_SHARED_OWNER : 0));
+ if (r < 0)
+ goto finish;
+ }
}
+
+ if (bad_opt)
+ p->opt = dns_resource_record_unref(p->opt);
}
p->question = question;
@@ -1830,6 +2195,30 @@ finish:
return r;
}
+int dns_packet_is_reply_for(DnsPacket *p, const DnsResourceKey *key) {
+ int r;
+
+ assert(p);
+ assert(key);
+
+ /* Checks if the specified packet is a reply for the specified
+ * key and the specified key is the only one in the question
+ * section. */
+
+ if (DNS_PACKET_QR(p) != 1)
+ return 0;
+
+ /* Let's unpack the packet, if that hasn't happened yet. */
+ r = dns_packet_extract(p);
+ if (r < 0)
+ return r;
+
+ if (p->question->n_keys != 1)
+ return 0;
+
+ return dns_resource_key_equal(p->question->keys[0], key);
+}
+
static const char* const dns_rcode_table[_DNS_RCODE_MAX_DEFINED] = {
[DNS_RCODE_SUCCESS] = "SUCCESS",
[DNS_RCODE_FORMERR] = "FORMERR",
@@ -1858,17 +2247,3 @@ static const char* const dns_protocol_table[_DNS_PROTOCOL_MAX] = {
[DNS_PROTOCOL_LLMNR] = "llmnr",
};
DEFINE_STRING_TABLE_LOOKUP(dns_protocol, DnsProtocol);
-
-static const char* const dnssec_algorithm_table[_DNSSEC_ALGORITHM_MAX_DEFINED] = {
- [DNSSEC_ALGORITHM_RSAMD5] = "RSAMD5",
- [DNSSEC_ALGORITHM_DH] = "DH",
- [DNSSEC_ALGORITHM_DSA] = "DSA",
- [DNSSEC_ALGORITHM_ECC] = "ECC",
- [DNSSEC_ALGORITHM_RSASHA1] = "RSASHA1",
- [DNSSEC_ALGORITHM_DSA_NSEC3_SHA1] = "DSA-NSEC3-SHA1",
- [DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1] = "RSASHA1-NSEC3-SHA1",
- [DNSSEC_ALGORITHM_INDIRECT] = "INDIRECT",
- [DNSSEC_ALGORITHM_PRIVATEDNS] = "PRIVATEDNS",
- [DNSSEC_ALGORITHM_PRIVATEOID] = "PRIVATEOID",
-};
-DEFINE_STRING_TABLE_LOOKUP(dnssec_algorithm, int);
diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h
index fbbabaf232..c53431576b 100644
--- a/src/resolve/resolved-dns-packet.h
+++ b/src/resolve/resolved-dns-packet.h
@@ -21,21 +21,21 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <netinet/udp.h>
#include <netinet/ip.h>
+#include <netinet/udp.h>
-#include "macro.h"
-#include "sparse-endian.h"
#include "hashmap.h"
#include "in-addr-util.h"
+#include "macro.h"
+#include "sparse-endian.h"
typedef struct DnsPacketHeader DnsPacketHeader;
typedef struct DnsPacket DnsPacket;
-#include "resolved-dns-rr.h"
-#include "resolved-dns-question.h"
-#include "resolved-dns-answer.h"
#include "resolved-def.h"
+#include "resolved-dns-answer.h"
+#include "resolved-dns-question.h"
+#include "resolved-dns-rr.h"
typedef enum DnsProtocol {
DNS_PROTOCOL_DNS,
@@ -65,6 +65,9 @@ struct DnsPacketHeader {
/* RFC 1035 say 512 is the maximum, for classic unicast DNS */
#define DNS_PACKET_UNICAST_SIZE_MAX 512
+/* With EDNS0 we can use larger packets, default to 4096, which is what is commonly used */
+#define DNS_PACKET_UNICAST_SIZE_LARGE_MAX 4096
+
#define DNS_PACKET_SIZE_START 512
struct DnsPacket {
@@ -73,10 +76,12 @@ struct DnsPacket {
size_t size, allocated, rindex;
void *_data; /* don't access directly, use DNS_PACKET_DATA()! */
Hashmap *names; /* For name compression */
+ size_t opt_start, opt_size;
/* Parsed data */
DnsQuestion *question;
DnsAnswer *answer;
+ DnsResourceRecord *opt;
/* Packet reception metadata */
int ifindex;
@@ -85,8 +90,13 @@ struct DnsPacket {
uint16_t sender_port, destination_port;
uint32_t ttl;
- bool extracted;
- bool refuse_compression;
+ /* For support of truncated packets */
+ DnsPacket *more;
+
+ bool on_stack:1;
+ bool extracted:1;
+ bool refuse_compression:1;
+ bool canonical_form:1;
};
static inline uint8_t* DNS_PACKET_DATA(DnsPacket *p) {
@@ -109,7 +119,17 @@ static inline uint8_t* DNS_PACKET_DATA(DnsPacket *p) {
#define DNS_PACKET_RA(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 7) & 1)
#define DNS_PACKET_AD(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 5) & 1)
#define DNS_PACKET_CD(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 4) & 1)
-#define DNS_PACKET_RCODE(p) (be16toh(DNS_PACKET_HEADER(p)->flags) & 15)
+
+static inline uint16_t DNS_PACKET_RCODE(DnsPacket *p) {
+ uint16_t rcode;
+
+ if (p->opt)
+ rcode = (uint16_t) (p->opt->ttl >> 24);
+ else
+ rcode = 0;
+
+ return rcode | (be16toh(DNS_PACKET_HEADER(p)->flags) & 15);
+}
/* LLMNR defines some bits differently */
#define DNS_PACKET_LLMNR_C(p) DNS_PACKET_AA(p)
@@ -139,7 +159,9 @@ static inline unsigned DNS_PACKET_RRCOUNT(DnsPacket *p) {
}
int dns_packet_new(DnsPacket **p, DnsProtocol protocol, size_t mtu);
-int dns_packet_new_query(DnsPacket **p, DnsProtocol protocol, size_t mtu);
+int dns_packet_new_query(DnsPacket **p, DnsProtocol protocol, size_t mtu, bool dnssec_checking_disabled);
+
+void dns_packet_set_flags(DnsPacket *p, bool dnssec_checking_disabled, bool truncated);
DnsPacket *dns_packet_ref(DnsPacket *p);
DnsPacket *dns_packet_unref(DnsPacket *p);
@@ -150,16 +172,22 @@ int dns_packet_validate(DnsPacket *p);
int dns_packet_validate_reply(DnsPacket *p);
int dns_packet_validate_query(DnsPacket *p);
+int dns_packet_is_reply_for(DnsPacket *p, const DnsResourceKey *key);
+
int dns_packet_append_blob(DnsPacket *p, const void *d, size_t sz, size_t *start);
int dns_packet_append_uint8(DnsPacket *p, uint8_t v, size_t *start);
int dns_packet_append_uint16(DnsPacket *p, uint16_t v, size_t *start);
int dns_packet_append_uint32(DnsPacket *p, uint32_t v, size_t *start);
int dns_packet_append_string(DnsPacket *p, const char *s, size_t *start);
-int dns_packet_append_label(DnsPacket *p, const char *s, size_t l, size_t *start);
-int dns_packet_append_name(DnsPacket *p, const char *name,
- bool allow_compression, size_t *start);
+int dns_packet_append_raw_string(DnsPacket *p, const void *s, size_t size, size_t *start);
+int dns_packet_append_label(DnsPacket *p, const char *s, size_t l, bool canonical_candidate, size_t *start);
+int dns_packet_append_name(DnsPacket *p, const char *name, bool allow_compression, bool canonical_candidate, size_t *start);
int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *key, size_t *start);
-int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *start);
+int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *start, size_t *rdata_start);
+int dns_packet_append_opt(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, size_t *start);
+
+void dns_packet_truncate(DnsPacket *p, size_t sz);
+int dns_packet_truncate_opt(DnsPacket *p);
int dns_packet_read(DnsPacket *p, size_t sz, const void **ret, size_t *start);
int dns_packet_read_blob(DnsPacket *p, void *d, size_t sz, size_t *start);
@@ -167,16 +195,25 @@ int dns_packet_read_uint8(DnsPacket *p, uint8_t *ret, size_t *start);
int dns_packet_read_uint16(DnsPacket *p, uint16_t *ret, size_t *start);
int dns_packet_read_uint32(DnsPacket *p, uint32_t *ret, size_t *start);
int dns_packet_read_string(DnsPacket *p, char **ret, size_t *start);
-int dns_packet_read_name(DnsPacket *p, char **ret,
- bool allow_compression, size_t *start);
-int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start);
-int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start);
+int dns_packet_read_raw_string(DnsPacket *p, const void **ret, size_t *size, size_t *start);
+int dns_packet_read_name(DnsPacket *p, char **ret, bool allow_compression, size_t *start);
+int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flush, size_t *start);
+int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_flush, size_t *start);
void dns_packet_rewind(DnsPacket *p, size_t idx);
int dns_packet_skip_question(DnsPacket *p);
int dns_packet_extract(DnsPacket *p);
+static inline bool DNS_PACKET_SHALL_CACHE(DnsPacket *p) {
+ /* Never cache data originating from localhost, under the
+ * assumption, that it's coming from a locally DNS forwarder
+ * or server, that is caching on its own. */
+
+ return in_addr_is_localhost(p->family, &p->sender) == 0;
+}
+
+/* https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-6 */
enum {
DNS_RCODE_SUCCESS = 0,
DNS_RCODE_FORMERR = 1,
@@ -209,42 +246,25 @@ DnsProtocol dns_protocol_from_string(const char *s) _pure_;
#define LLMNR_MULTICAST_IPV4_ADDRESS ((struct in_addr) { .s_addr = htobe32(224U << 24 | 252U) })
#define LLMNR_MULTICAST_IPV6_ADDRESS ((struct in6_addr) { .s6_addr = { 0xFF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03 } })
-#define DNSKEY_FLAG_ZONE_KEY (1u << 8)
-#define DNSKEY_FLAG_SEP (1u << 0)
+#define MDNS_MULTICAST_IPV4_ADDRESS ((struct in_addr) { .s_addr = htobe32(224U << 24 | 251U) })
+#define MDNS_MULTICAST_IPV6_ADDRESS ((struct in6_addr) { .s6_addr = { 0xFF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb } })
-static inline uint16_t dnskey_to_flags(const DnsResourceRecord *rr) {
- return (rr->dnskey.zone_key_flag * DNSKEY_FLAG_ZONE_KEY |
- rr->dnskey.sep_flag * DNSKEY_FLAG_SEP);
-}
+static inline uint64_t SD_RESOLVED_FLAGS_MAKE(DnsProtocol protocol, int family, bool authenticated) {
+ uint64_t f;
-/* http://tools.ietf.org/html/rfc4034#appendix-A.1 */
-enum {
- DNSSEC_ALGORITHM_RSAMD5 = 1,
- DNSSEC_ALGORITHM_DH,
- DNSSEC_ALGORITHM_DSA,
- DNSSEC_ALGORITHM_ECC,
- DNSSEC_ALGORITHM_RSASHA1,
- DNSSEC_ALGORITHM_DSA_NSEC3_SHA1,
- DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1,
- DNSSEC_ALGORITHM_INDIRECT = 252,
- DNSSEC_ALGORITHM_PRIVATEDNS,
- DNSSEC_ALGORITHM_PRIVATEOID,
- _DNSSEC_ALGORITHM_MAX_DEFINED
-};
+ /* Converts a protocol + family into a flags field as used in queries and responses */
-const char* dnssec_algorithm_to_string(int i) _const_;
-int dnssec_algorithm_from_string(const char *s) _pure_;
-
-static inline uint64_t SD_RESOLVED_FLAGS_MAKE(DnsProtocol protocol, int family) {
-
- /* Converts a protocol + family into a flags field as used in queries */
+ f = authenticated ? SD_RESOLVED_AUTHENTICATED : 0;
switch (protocol) {
case DNS_PROTOCOL_DNS:
- return SD_RESOLVED_DNS;
+ return f|SD_RESOLVED_DNS;
case DNS_PROTOCOL_LLMNR:
- return family == AF_INET6 ? SD_RESOLVED_LLMNR_IPV6 : SD_RESOLVED_LLMNR_IPV4;
+ return f|(family == AF_INET6 ? SD_RESOLVED_LLMNR_IPV6 : SD_RESOLVED_LLMNR_IPV4);
+
+ case DNS_PROTOCOL_MDNS:
+ return family == AF_INET6 ? SD_RESOLVED_MDNS_IPV6 : SD_RESOLVED_MDNS_IPV4;
default:
break;
diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c
index 4b1d18b2ef..fc5bf4020f 100644
--- a/src/resolve/resolved-dns-query.c
+++ b/src/resolve/resolved-dns-query.c
@@ -19,44 +19,340 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "hostname-util.h"
+#include "alloc-util.h"
#include "dns-domain.h"
+#include "hostname-util.h"
#include "local-addresses.h"
-
#include "resolved-dns-query.h"
+#include "string-util.h"
/* How long to wait for the query in total */
#define QUERY_TIMEOUT_USEC (30 * USEC_PER_SEC)
#define CNAME_MAX 8
#define QUERIES_MAX 2048
+#define AUXILIARY_QUERIES_MAX 64
-static void dns_query_stop(DnsQuery *q) {
- DnsTransaction *t;
+static int dns_query_candidate_new(DnsQueryCandidate **ret, DnsQuery *q, DnsScope *s) {
+ DnsQueryCandidate *c;
+ assert(ret);
assert(q);
+ assert(s);
- q->timeout_event_source = sd_event_source_unref(q->timeout_event_source);
+ c = new0(DnsQueryCandidate, 1);
+ if (!c)
+ return -ENOMEM;
+
+ c->query = q;
+ c->scope = s;
+
+ LIST_PREPEND(candidates_by_query, q->candidates, c);
+ LIST_PREPEND(candidates_by_scope, s->query_candidates, c);
+
+ *ret = c;
+ return 0;
+}
- while ((t = set_steal_first(q->transactions))) {
- set_remove(t->queries, q);
+static void dns_query_candidate_stop(DnsQueryCandidate *c) {
+ DnsTransaction *t;
+
+ assert(c);
+
+ while ((t = set_steal_first(c->transactions))) {
+ set_remove(t->notify_query_candidates, c);
dns_transaction_gc(t);
}
}
+DnsQueryCandidate* dns_query_candidate_free(DnsQueryCandidate *c) {
+
+ if (!c)
+ return NULL;
+
+ dns_query_candidate_stop(c);
+
+ set_free(c->transactions);
+ dns_search_domain_unref(c->search_domain);
+
+ if (c->query)
+ LIST_REMOVE(candidates_by_query, c->query->candidates, c);
+
+ if (c->scope)
+ LIST_REMOVE(candidates_by_scope, c->scope->query_candidates, c);
+
+ free(c);
+
+ return NULL;
+}
+
+static int dns_query_candidate_next_search_domain(DnsQueryCandidate *c) {
+ DnsSearchDomain *next = NULL;
+
+ assert(c);
+
+ if (c->search_domain && c->search_domain->linked) {
+ next = c->search_domain->domains_next;
+
+ if (!next) /* We hit the end of the list */
+ return 0;
+
+ } else {
+ next = dns_scope_get_search_domains(c->scope);
+
+ if (!next) /* OK, there's nothing. */
+ return 0;
+ }
+
+ dns_search_domain_unref(c->search_domain);
+ c->search_domain = dns_search_domain_ref(next);
+
+ return 1;
+}
+
+static int dns_query_candidate_add_transaction(DnsQueryCandidate *c, DnsResourceKey *key) {
+ DnsTransaction *t;
+ int r;
+
+ assert(c);
+ assert(key);
+
+ t = dns_scope_find_transaction(c->scope, key, true);
+ if (!t) {
+ r = dns_transaction_new(&t, c->scope, key);
+ if (r < 0)
+ return r;
+ } else {
+ if (set_contains(c->transactions, t))
+ return 0;
+ }
+
+ r = set_ensure_allocated(&c->transactions, NULL);
+ if (r < 0)
+ goto gc;
+
+ r = set_ensure_allocated(&t->notify_query_candidates, NULL);
+ if (r < 0)
+ goto gc;
+
+ r = set_put(t->notify_query_candidates, c);
+ if (r < 0)
+ goto gc;
+
+ r = set_put(c->transactions, t);
+ if (r < 0) {
+ (void) set_remove(t->notify_query_candidates, c);
+ goto gc;
+ }
+
+ return 1;
+
+gc:
+ dns_transaction_gc(t);
+ return r;
+}
+
+static int dns_query_candidate_go(DnsQueryCandidate *c) {
+ DnsTransaction *t;
+ Iterator i;
+ int r;
+
+ assert(c);
+
+ /* Start the transactions that are not started yet */
+ SET_FOREACH(t, c->transactions, i) {
+ if (t->state != DNS_TRANSACTION_NULL)
+ continue;
+
+ r = dns_transaction_go(t);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static DnsTransactionState dns_query_candidate_state(DnsQueryCandidate *c) {
+ DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
+ DnsTransaction *t;
+ Iterator i;
+
+ assert(c);
+
+ if (c->error_code != 0)
+ return DNS_TRANSACTION_RESOURCES;
+
+ SET_FOREACH(t, c->transactions, i) {
+
+ switch (t->state) {
+
+ case DNS_TRANSACTION_NULL:
+ /* If there's a NULL transaction pending, then
+ * this means not all transactions where
+ * started yet, and we were called from within
+ * the stackframe that is supposed to start
+ * remaining transactions. In this case,
+ * simply claim the candidate is pending. */
+
+ case DNS_TRANSACTION_PENDING:
+ case DNS_TRANSACTION_VALIDATING:
+ /* If there's one transaction currently in
+ * VALIDATING state, then this means there's
+ * also one in PENDING state, hence we can
+ * return PENDING immediately. */
+ return DNS_TRANSACTION_PENDING;
+
+ case DNS_TRANSACTION_SUCCESS:
+ state = t->state;
+ break;
+
+ default:
+ if (state != DNS_TRANSACTION_SUCCESS)
+ state = t->state;
+
+ break;
+ }
+ }
+
+ return state;
+}
+
+static int dns_query_candidate_setup_transactions(DnsQueryCandidate *c) {
+ DnsQuestion *question;
+ DnsResourceKey *key;
+ int n = 0, r;
+
+ assert(c);
+
+ dns_query_candidate_stop(c);
+
+ question = dns_query_question_for_protocol(c->query, c->scope->protocol);
+
+ /* Create one transaction per question key */
+ DNS_QUESTION_FOREACH(key, question) {
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *new_key = NULL;
+
+ if (c->search_domain) {
+ r = dns_resource_key_new_append_suffix(&new_key, key, c->search_domain->name);
+ if (r < 0)
+ goto fail;
+ }
+
+ r = dns_query_candidate_add_transaction(c, new_key ?: key);
+ if (r < 0)
+ goto fail;
+
+ n++;
+ }
+
+ return n;
+
+fail:
+ dns_query_candidate_stop(c);
+ return r;
+}
+
+void dns_query_candidate_notify(DnsQueryCandidate *c) {
+ DnsTransactionState state;
+ int r;
+
+ assert(c);
+
+ state = dns_query_candidate_state(c);
+
+ if (DNS_TRANSACTION_IS_LIVE(state))
+ return;
+
+ if (state != DNS_TRANSACTION_SUCCESS && c->search_domain) {
+
+ r = dns_query_candidate_next_search_domain(c);
+ if (r < 0)
+ goto fail;
+
+ if (r > 0) {
+ /* OK, there's another search domain to try, let's do so. */
+
+ r = dns_query_candidate_setup_transactions(c);
+ if (r < 0)
+ goto fail;
+
+ if (r > 0) {
+ /* New transactions where queued. Start them and wait */
+
+ r = dns_query_candidate_go(c);
+ if (r < 0)
+ goto fail;
+
+ return;
+ }
+ }
+
+ }
+
+ dns_query_ready(c->query);
+ return;
+
+fail:
+ log_warning_errno(r, "Failed to follow search domains: %m");
+ c->error_code = r;
+ dns_query_ready(c->query);
+}
+
+static void dns_query_stop(DnsQuery *q) {
+ DnsQueryCandidate *c;
+
+ assert(q);
+
+ q->timeout_event_source = sd_event_source_unref(q->timeout_event_source);
+
+ LIST_FOREACH(candidates_by_query, c, q->candidates)
+ dns_query_candidate_stop(c);
+}
+
+static void dns_query_free_candidates(DnsQuery *q) {
+ assert(q);
+
+ while (q->candidates)
+ dns_query_candidate_free(q->candidates);
+}
+
+static void dns_query_reset_answer(DnsQuery *q) {
+ assert(q);
+
+ q->answer = dns_answer_unref(q->answer);
+ q->answer_rcode = 0;
+ q->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
+ q->answer_authenticated = false;
+ q->answer_protocol = _DNS_PROTOCOL_INVALID;
+ q->answer_family = AF_UNSPEC;
+ q->answer_search_domain = dns_search_domain_unref(q->answer_search_domain);
+}
+
DnsQuery *dns_query_free(DnsQuery *q) {
if (!q)
return NULL;
- dns_query_stop(q);
- set_free(q->transactions);
+ while (q->auxiliary_queries)
+ dns_query_free(q->auxiliary_queries);
- dns_question_unref(q->question);
- dns_answer_unref(q->answer);
+ if (q->auxiliary_for) {
+ assert(q->auxiliary_for->n_auxiliary_queries > 0);
+ q->auxiliary_for->n_auxiliary_queries--;
+ LIST_REMOVE(auxiliary_queries, q->auxiliary_for->auxiliary_queries, q);
+ }
+
+ dns_query_free_candidates(q);
+
+ dns_question_unref(q->question_idna);
+ dns_question_unref(q->question_utf8);
+
+ dns_query_reset_answer(q);
sd_bus_message_unref(q->request);
sd_bus_track_unref(q->bus_track);
+ free(q->request_address_string);
+
if (q->manager) {
LIST_REMOVE(queries, q->manager->dns_queries, q);
q->manager->n_dns_queries--;
@@ -67,17 +363,50 @@ DnsQuery *dns_query_free(DnsQuery *q) {
return NULL;
}
-int dns_query_new(Manager *m, DnsQuery **ret, DnsQuestion *question, int ifindex, uint64_t flags) {
+int dns_query_new(
+ Manager *m,
+ DnsQuery **ret,
+ DnsQuestion *question_utf8,
+ DnsQuestion *question_idna,
+ int ifindex, uint64_t flags) {
+
_cleanup_(dns_query_freep) DnsQuery *q = NULL;
- unsigned i;
+ DnsResourceKey *key;
+ bool good = false;
int r;
assert(m);
- assert(question);
- r = dns_question_is_valid(question);
+ if (dns_question_size(question_utf8) > 0) {
+ r = dns_question_is_valid_for_query(question_utf8);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EINVAL;
+
+ good = true;
+ }
+
+ /* If the IDNA and UTF8 questions are the same, merge their references */
+ r = dns_question_is_equal(question_idna, question_utf8);
if (r < 0)
return r;
+ if (r > 0)
+ question_idna = question_utf8;
+ else {
+ if (dns_question_size(question_idna) > 0) {
+ r = dns_question_is_valid_for_query(question_idna);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EINVAL;
+
+ good = true;
+ }
+ }
+
+ if (!good) /* don't allow empty queries */
+ return -EINVAL;
if (m->n_dns_queries >= QUERIES_MAX)
return -EBUSY;
@@ -86,18 +415,40 @@ int dns_query_new(Manager *m, DnsQuery **ret, DnsQuestion *question, int ifindex
if (!q)
return -ENOMEM;
- q->question = dns_question_ref(question);
+ q->question_utf8 = dns_question_ref(question_utf8);
+ q->question_idna = dns_question_ref(question_idna);
q->ifindex = ifindex;
q->flags = flags;
+ q->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
+ q->answer_protocol = _DNS_PROTOCOL_INVALID;
+ q->answer_family = AF_UNSPEC;
- for (i = 0; i < question->n_keys; i++) {
- _cleanup_free_ char *p;
+ /* First dump UTF8 question */
+ DNS_QUESTION_FOREACH(key, question_utf8) {
+ _cleanup_free_ char *p = NULL;
- r = dns_resource_key_to_string(question->keys[i], &p);
+ r = dns_resource_key_to_string(key, &p);
if (r < 0)
return r;
- log_debug("Looking up RR for %s", p);
+ log_debug("Looking up RR for %s.", strstrip(p));
+ }
+
+ /* And then dump the IDNA question, but only what hasn't been dumped already through the UTF8 question. */
+ DNS_QUESTION_FOREACH(key, question_idna) {
+ _cleanup_free_ char *p = NULL;
+
+ r = dns_question_contains(question_utf8, key);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ continue;
+
+ r = dns_resource_key_to_string(key, &p);
+ if (r < 0)
+ return r;
+
+ log_debug("Looking up IDNA RR for %s.", strstrip(p));
}
LIST_PREPEND(queries, m->dns_queries, q);
@@ -111,10 +462,33 @@ int dns_query_new(Manager *m, DnsQuery **ret, DnsQuestion *question, int ifindex
return 0;
}
+int dns_query_make_auxiliary(DnsQuery *q, DnsQuery *auxiliary_for) {
+ assert(q);
+ assert(auxiliary_for);
+
+ /* Ensure that that the query is not auxiliary yet, and
+ * nothing else is auxiliary to it either */
+ assert(!q->auxiliary_for);
+ assert(!q->auxiliary_queries);
+
+ /* Ensure that the unit we shall be made auxiliary for isn't
+ * auxiliary itself */
+ assert(!auxiliary_for->auxiliary_for);
+
+ if (auxiliary_for->n_auxiliary_queries >= AUXILIARY_QUERIES_MAX)
+ return -EAGAIN;
+
+ LIST_PREPEND(auxiliary_queries, auxiliary_for->auxiliary_queries, q);
+ q->auxiliary_for = auxiliary_for;
+
+ auxiliary_for->n_auxiliary_queries++;
+ return 0;
+}
+
static void dns_query_complete(DnsQuery *q, DnsTransactionState state) {
assert(q);
- assert(!IN_SET(state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
- assert(IN_SET(q->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
+ assert(!DNS_TRANSACTION_IS_LIVE(state));
+ assert(DNS_TRANSACTION_IS_LIVE(q->state));
/* Note that this call might invalidate the query. Callers
* should hence not attempt to access the query or transaction
@@ -137,64 +511,42 @@ static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) {
return 0;
}
-static int dns_query_add_transaction(DnsQuery *q, DnsScope *s, DnsResourceKey *key) {
- DnsTransaction *t;
+static int dns_query_add_candidate(DnsQuery *q, DnsScope *s) {
+ DnsQueryCandidate *c;
int r;
assert(q);
assert(s);
- assert(key);
- r = set_ensure_allocated(&q->transactions, NULL);
+ r = dns_query_candidate_new(&c, q, s);
if (r < 0)
return r;
- t = dns_scope_find_transaction(s, key, true);
- if (!t) {
- r = dns_transaction_new(&t, s, key);
+ /* If this a single-label domain on DNS, we might append a suitable search domain first. */
+ if ((q->flags & SD_RESOLVED_NO_SEARCH) == 0) {
+ r = dns_scope_name_needs_search_domain(s, dns_question_first_name(q->question_idna));
if (r < 0)
- return r;
- }
+ goto fail;
+ if (r > 0) {
+ /* OK, we need a search domain now. Let's find one for this scope */
- r = set_ensure_allocated(&t->queries, NULL);
- if (r < 0)
- goto gc;
+ r = dns_query_candidate_next_search_domain(c);
+ if (r <= 0) /* if there's no search domain, then we won't add any transaction. */
+ goto fail;
+ }
+ }
- r = set_put(t->queries, q);
+ r = dns_query_candidate_setup_transactions(c);
if (r < 0)
- goto gc;
-
- r = set_put(q->transactions, t);
- if (r < 0) {
- set_remove(t->queries, q);
- goto gc;
- }
+ goto fail;
return 0;
-gc:
- dns_transaction_gc(t);
+fail:
+ dns_query_candidate_free(c);
return r;
}
-static int dns_query_add_transaction_split(DnsQuery *q, DnsScope *s) {
- unsigned i;
- int r;
-
- assert(q);
- assert(s);
-
- /* Create one transaction per question key */
-
- for (i = 0; i < q->question->n_keys; i++) {
- r = dns_query_add_transaction(q, s, q->question->keys[i]);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
static int SYNTHESIZE_IFINDEX(int ifindex) {
/* When the caller asked for resolving on a specific
@@ -260,7 +612,7 @@ static int dns_type_to_af(uint16_t t) {
}
}
-static int synthesize_localhost_rr(DnsQuery *q, DnsResourceKey *key, DnsAnswer **answer) {
+static int synthesize_localhost_rr(DnsQuery *q, const DnsResourceKey *key, DnsAnswer **answer) {
int r;
assert(q);
@@ -280,7 +632,7 @@ static int synthesize_localhost_rr(DnsQuery *q, DnsResourceKey *key, DnsAnswer *
rr->a.in_addr.s_addr = htobe32(INADDR_LOOPBACK);
- r = dns_answer_add(*answer, rr, SYNTHESIZE_IFINDEX(q->ifindex));
+ r = dns_answer_add(*answer, rr, SYNTHESIZE_IFINDEX(q->ifindex), DNS_ANSWER_AUTHENTICATED);
if (r < 0)
return r;
}
@@ -294,7 +646,7 @@ static int synthesize_localhost_rr(DnsQuery *q, DnsResourceKey *key, DnsAnswer *
rr->aaaa.in6_addr = in6addr_loopback;
- r = dns_answer_add(*answer, rr, SYNTHESIZE_IFINDEX(q->ifindex));
+ r = dns_answer_add(*answer, rr, SYNTHESIZE_IFINDEX(q->ifindex), DNS_ANSWER_AUTHENTICATED);
if (r < 0)
return r;
}
@@ -302,7 +654,7 @@ static int synthesize_localhost_rr(DnsQuery *q, DnsResourceKey *key, DnsAnswer *
return 0;
}
-static int answer_add_ptr(DnsAnswer **answer, const char *from, const char *to, int ifindex) {
+static int answer_add_ptr(DnsAnswer **answer, const char *from, const char *to, int ifindex, DnsAnswerFlags flags) {
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_PTR, from);
@@ -313,22 +665,22 @@ static int answer_add_ptr(DnsAnswer **answer, const char *from, const char *to,
if (!rr->ptr.name)
return -ENOMEM;
- return dns_answer_add(*answer, rr, ifindex);
+ return dns_answer_add(*answer, rr, ifindex, flags);
}
-static int synthesize_localhost_ptr(DnsQuery *q, DnsResourceKey *key, DnsAnswer **answer) {
+static int synthesize_localhost_ptr(DnsQuery *q, const DnsResourceKey *key, DnsAnswer **answer) {
int r;
assert(q);
assert(key);
assert(answer);
- r = dns_answer_reserve(answer, 1);
- if (r < 0)
- return r;
-
if (IN_SET(key->type, DNS_TYPE_PTR, DNS_TYPE_ANY)) {
- r = answer_add_ptr(answer, DNS_RESOURCE_KEY_NAME(key), "localhost", SYNTHESIZE_IFINDEX(q->ifindex));
+ r = dns_answer_reserve(answer, 1);
+ if (r < 0)
+ return r;
+
+ r = answer_add_ptr(answer, DNS_RESOURCE_KEY_NAME(key), "localhost", SYNTHESIZE_IFINDEX(q->ifindex), DNS_ANSWER_AUTHENTICATED);
if (r < 0)
return r;
}
@@ -359,7 +711,7 @@ static int answer_add_addresses_rr(
if (r < 0)
return r;
- r = dns_answer_add(*answer, rr, addresses[j].ifindex);
+ r = dns_answer_add(*answer, rr, addresses[j].ifindex, DNS_ANSWER_AUTHENTICATED);
if (r < 0)
return r;
}
@@ -400,7 +752,7 @@ static int answer_add_addresses_ptr(
if (r < 0)
return r;
- r = dns_answer_add(*answer, rr, addresses[j].ifindex);
+ r = dns_answer_add(*answer, rr, addresses[j].ifindex, DNS_ANSWER_AUTHENTICATED);
if (r < 0)
return r;
}
@@ -408,7 +760,7 @@ static int answer_add_addresses_ptr(
return 0;
}
-static int synthesize_system_hostname_rr(DnsQuery *q, DnsResourceKey *key, DnsAnswer **answer) {
+static int synthesize_system_hostname_rr(DnsQuery *q, const DnsResourceKey *key, DnsAnswer **answer) {
_cleanup_free_ struct local_address *addresses = NULL;
int n = 0, af;
@@ -466,15 +818,15 @@ static int synthesize_system_hostname_ptr(DnsQuery *q, int af, const union in_ad
if (r < 0)
return r;
- r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", q->manager->llmnr_hostname, SYNTHESIZE_IFINDEX(q->ifindex));
+ r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", q->manager->llmnr_hostname, SYNTHESIZE_IFINDEX(q->ifindex), DNS_ANSWER_AUTHENTICATED);
if (r < 0)
return r;
- r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", q->manager->mdns_hostname, SYNTHESIZE_IFINDEX(q->ifindex));
+ r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", q->manager->mdns_hostname, SYNTHESIZE_IFINDEX(q->ifindex), DNS_ANSWER_AUTHENTICATED);
if (r < 0)
return r;
- r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", "localhost", SYNTHESIZE_IFINDEX(q->ifindex));
+ r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", "localhost", SYNTHESIZE_IFINDEX(q->ifindex), DNS_ANSWER_AUTHENTICATED);
if (r < 0)
return r;
@@ -492,7 +844,7 @@ static int synthesize_system_hostname_ptr(DnsQuery *q, int af, const union in_ad
return answer_add_addresses_ptr(answer, q->manager->mdns_hostname, addresses, n, af, address);
}
-static int synthesize_gateway_rr(DnsQuery *q, DnsResourceKey *key, DnsAnswer **answer) {
+static int synthesize_gateway_rr(DnsQuery *q, const DnsResourceKey *key, DnsAnswer **answer) {
_cleanup_free_ struct local_address *addresses = NULL;
int n = 0, af;
@@ -527,7 +879,7 @@ static int synthesize_gateway_ptr(DnsQuery *q, int af, const union in_addr_union
static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) {
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
- unsigned i;
+ DnsResourceKey *key;
int r;
assert(q);
@@ -536,45 +888,45 @@ static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) {
/* Tries to synthesize localhost RR replies where appropriate */
if (!IN_SET(*state,
- DNS_TRANSACTION_FAILURE,
+ DNS_TRANSACTION_RCODE_FAILURE,
DNS_TRANSACTION_NO_SERVERS,
DNS_TRANSACTION_TIMEOUT,
DNS_TRANSACTION_ATTEMPTS_MAX_REACHED))
return 0;
- for (i = 0; i < q->question->n_keys; i++) {
+ DNS_QUESTION_FOREACH(key, q->question_utf8) {
union in_addr_union address;
const char *name;
int af;
- if (q->question->keys[i]->class != DNS_CLASS_IN &&
- q->question->keys[i]->class != DNS_CLASS_ANY)
+ if (key->class != DNS_CLASS_IN &&
+ key->class != DNS_CLASS_ANY)
continue;
- name = DNS_RESOURCE_KEY_NAME(q->question->keys[i]);
+ name = DNS_RESOURCE_KEY_NAME(key);
if (is_localhost(name)) {
- r = synthesize_localhost_rr(q, q->question->keys[i], &answer);
+ r = synthesize_localhost_rr(q, key, &answer);
if (r < 0)
return log_error_errno(r, "Failed to synthesize localhost RRs: %m");
} else if (manager_is_own_hostname(q->manager, name)) {
- r = synthesize_system_hostname_rr(q, q->question->keys[i], &answer);
+ r = synthesize_system_hostname_rr(q, key, &answer);
if (r < 0)
return log_error_errno(r, "Failed to synthesize system hostname RRs: %m");
} else if (is_gateway_hostname(name)) {
- r = synthesize_gateway_rr(q, q->question->keys[i], &answer);
+ r = synthesize_gateway_rr(q, key, &answer);
if (r < 0)
return log_error_errno(r, "Failed to synthesize gateway RRs: %m");
} else if ((dns_name_endswith(name, "127.in-addr.arpa") > 0 && dns_name_equal(name, "2.0.0.127.in-addr.arpa") == 0) ||
dns_name_equal(name, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0) {
- r = synthesize_localhost_ptr(q, q->question->keys[i], &answer);
+ r = synthesize_localhost_ptr(q, key, &answer);
if (r < 0)
return log_error_errno(r, "Failed to synthesize localhost PTR RRs: %m");
@@ -597,9 +949,9 @@ static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) {
q->answer = answer;
answer = NULL;
- q->answer_family = SYNTHESIZE_FAMILY(q->flags);
- q->answer_protocol = SYNTHESIZE_PROTOCOL(q->flags);
q->answer_rcode = DNS_RCODE_SUCCESS;
+ q->answer_protocol = SYNTHESIZE_PROTOCOL(q->flags);
+ q->answer_family = SYNTHESIZE_FAMILY(q->flags);
*state = DNS_TRANSACTION_SUCCESS;
@@ -609,9 +961,7 @@ static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) {
int dns_query_go(DnsQuery *q) {
DnsScopeMatch found = DNS_SCOPE_NO;
DnsScope *s, *first = NULL;
- DnsTransaction *t;
- const char *name;
- Iterator i;
+ DnsQueryCandidate *c;
int r;
assert(q);
@@ -619,13 +969,13 @@ int dns_query_go(DnsQuery *q) {
if (q->state != DNS_TRANSACTION_NULL)
return 0;
- assert(q->question);
- assert(q->question->n_keys > 0);
-
- name = DNS_RESOURCE_KEY_NAME(q->question->keys[0]);
-
LIST_FOREACH(scopes, s, q->manager->dns_scopes) {
DnsScopeMatch match;
+ const char *name;
+
+ name = dns_question_first_name(dns_query_question_for_protocol(q, s->protocol));
+ if (!name)
+ continue;
match = dns_scope_good_domain(s, q->ifindex, q->flags, name);
if (match < 0)
@@ -655,12 +1005,17 @@ int dns_query_go(DnsQuery *q) {
return 1;
}
- r = dns_query_add_transaction_split(q, first);
+ r = dns_query_add_candidate(q, first);
if (r < 0)
goto fail;
LIST_FOREACH(scopes, s, first->scopes_next) {
DnsScopeMatch match;
+ const char *name;
+
+ name = dns_question_first_name(dns_query_question_for_protocol(q, s->protocol));
+ if (!name)
+ continue;
match = dns_scope_good_domain(s, q->ifindex, q->flags, name);
if (match < 0)
@@ -669,7 +1024,7 @@ int dns_query_go(DnsQuery *q) {
if (match != found)
continue;
- r = dns_query_add_transaction_split(q, s);
+ r = dns_query_add_candidate(q, s);
if (r < 0)
goto fail;
}
@@ -688,17 +1043,18 @@ int dns_query_go(DnsQuery *q) {
if (r < 0)
goto fail;
+ (void) sd_event_source_set_description(q->timeout_event_source, "query-timeout");
+
q->state = DNS_TRANSACTION_PENDING;
q->block_ready++;
- /* Start the transactions that are not started yet */
- SET_FOREACH(t, q->transactions, i) {
- if (t->state != DNS_TRANSACTION_NULL)
- continue;
-
- r = dns_transaction_go(t);
- if (r < 0)
+ /* Start the transactions */
+ LIST_FOREACH(candidates_by_query, c, q->candidates) {
+ r = dns_query_candidate_go(c);
+ if (r < 0) {
+ q->block_ready--;
goto fail;
+ }
}
q->block_ready--;
@@ -711,151 +1067,239 @@ fail:
return r;
}
-void dns_query_ready(DnsQuery *q) {
- DnsTransaction *t;
+static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
- _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
- int rcode = 0;
- DnsScope *scope = NULL;
- bool pending = false;
+ bool has_authenticated = false, has_non_authenticated = false;
+ DnssecResult dnssec_result_authenticated = _DNSSEC_RESULT_INVALID, dnssec_result_non_authenticated = _DNSSEC_RESULT_INVALID;
+ DnsTransaction *t;
Iterator i;
+ int r;
assert(q);
- assert(IN_SET(q->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
-
- /* Note that this call might invalidate the query. Callers
- * should hence not attempt to access the query or transaction
- * after calling this function, unless the block_ready
- * counter was explicitly bumped before doing so. */
- if (q->block_ready > 0)
+ if (!c) {
+ dns_query_synthesize_reply(q, &state);
+ dns_query_complete(q, state);
return;
+ }
- SET_FOREACH(t, q->transactions, i) {
-
- /* If we found a successful answer, ignore all answers from other scopes */
- if (state == DNS_TRANSACTION_SUCCESS && t->scope != scope)
- continue;
-
- /* One of the transactions is still going on, let's maybe wait for it */
- if (IN_SET(t->state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_NULL)) {
- pending = true;
- continue;
- }
+ SET_FOREACH(t, c->transactions, i) {
- /* One of the transactions is successful, let's use
- * it, and copy its data out */
- if (t->state == DNS_TRANSACTION_SUCCESS) {
- DnsAnswer *a;
+ switch (t->state) {
- if (t->received) {
- rcode = DNS_PACKET_RCODE(t->received);
- a = t->received->answer;
- } else {
- rcode = t->cached_rcode;
- a = t->cached;
+ case DNS_TRANSACTION_SUCCESS: {
+ /* We found a successfuly reply, merge it into the answer */
+ r = dns_answer_extend(&q->answer, t->answer);
+ if (r < 0) {
+ dns_query_complete(q, DNS_TRANSACTION_RESOURCES);
+ return;
}
- if (state == DNS_TRANSACTION_SUCCESS) {
- DnsAnswer *merged;
+ q->answer_rcode = t->answer_rcode;
- merged = dns_answer_merge(answer, a);
- if (!merged) {
- dns_query_complete(q, DNS_TRANSACTION_RESOURCES);
- return;
- }
-
- dns_answer_unref(answer);
- answer = merged;
+ if (t->answer_authenticated) {
+ has_authenticated = true;
+ dnssec_result_authenticated = t->answer_dnssec_result;
} else {
- dns_answer_unref(answer);
- answer = dns_answer_ref(a);
+ has_non_authenticated = true;
+ dnssec_result_non_authenticated = t->answer_dnssec_result;
}
- scope = t->scope;
state = DNS_TRANSACTION_SUCCESS;
- continue;
+ break;
}
- /* One of the transactions has failed, let's see
- * whether we find anything better, but if not, return
- * its response data */
- if (state != DNS_TRANSACTION_SUCCESS && t->state == DNS_TRANSACTION_FAILURE) {
- DnsAnswer *a;
+ case DNS_TRANSACTION_NULL:
+ case DNS_TRANSACTION_PENDING:
+ case DNS_TRANSACTION_VALIDATING:
+ case DNS_TRANSACTION_ABORTED:
+ /* Ignore transactions that didn't complete */
+ continue;
- if (t->received) {
- rcode = DNS_PACKET_RCODE(t->received);
- a = t->received->answer;
- } else {
- rcode = t->cached_rcode;
- a = t->cached;
- }
+ default:
+ /* Any kind of failure? Store the data away,
+ * if there's nothing stored yet. */
- dns_answer_unref(answer);
- answer = dns_answer_ref(a);
+ if (state == DNS_TRANSACTION_SUCCESS)
+ continue;
- scope = t->scope;
- state = DNS_TRANSACTION_FAILURE;
- continue;
- }
+ q->answer = dns_answer_unref(q->answer);
+ q->answer_rcode = t->answer_rcode;
+ q->answer_dnssec_result = t->answer_dnssec_result;
- if (state == DNS_TRANSACTION_NO_SERVERS && t->state != DNS_TRANSACTION_NO_SERVERS)
state = t->state;
+ break;
+ }
+ }
+
+ if (state == DNS_TRANSACTION_SUCCESS) {
+ q->answer_authenticated = has_authenticated && !has_non_authenticated;
+ q->answer_dnssec_result = q->answer_authenticated ? dnssec_result_authenticated : dnssec_result_non_authenticated;
}
- if (pending) {
+ q->answer_protocol = c->scope->protocol;
+ q->answer_family = c->scope->family;
- /* If so far we weren't successful, and there's
- * something still pending, then wait for it */
- if (state != DNS_TRANSACTION_SUCCESS)
+ dns_search_domain_unref(q->answer_search_domain);
+ q->answer_search_domain = dns_search_domain_ref(c->search_domain);
+
+ dns_query_synthesize_reply(q, &state);
+ dns_query_complete(q, state);
+}
+
+void dns_query_ready(DnsQuery *q) {
+
+ DnsQueryCandidate *bad = NULL, *c;
+ bool pending = false;
+
+ assert(q);
+ assert(DNS_TRANSACTION_IS_LIVE(q->state));
+
+ /* Note that this call might invalidate the query. Callers
+ * should hence not attempt to access the query or transaction
+ * after calling this function, unless the block_ready
+ * counter was explicitly bumped before doing so. */
+
+ if (q->block_ready > 0)
+ return;
+
+ LIST_FOREACH(candidates_by_query, c, q->candidates) {
+ DnsTransactionState state;
+
+ state = dns_query_candidate_state(c);
+ switch (state) {
+
+ case DNS_TRANSACTION_SUCCESS:
+ /* One of the candidates is successful,
+ * let's use it, and copy its data out */
+ dns_query_accept(q, c);
return;
- /* If we already were successful, then only wait for
- * other transactions on the same scope to finish. */
- SET_FOREACH(t, q->transactions, i) {
- if (t->scope == scope && IN_SET(t->state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_NULL))
- return;
- }
- }
+ case DNS_TRANSACTION_NULL:
+ case DNS_TRANSACTION_PENDING:
+ case DNS_TRANSACTION_VALIDATING:
+ /* One of the candidates is still going on,
+ * let's maybe wait for it */
+ pending = true;
+ break;
- if (IN_SET(state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_FAILURE)) {
- q->answer = dns_answer_ref(answer);
- q->answer_rcode = rcode;
- q->answer_protocol = scope ? scope->protocol : _DNS_PROTOCOL_INVALID;
- q->answer_family = scope ? scope->family : AF_UNSPEC;
+ default:
+ /* Any kind of failure */
+ bad = c;
+ break;
+ }
}
- /* Try to synthesize a reply if we couldn't resolve something. */
- dns_query_synthesize_reply(q, &state);
+ if (pending)
+ return;
- dns_query_complete(q, state);
+ dns_query_accept(q, bad);
}
-int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname) {
- _cleanup_(dns_question_unrefp) DnsQuestion *nq = NULL;
- int r;
+static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname) {
+ _cleanup_(dns_question_unrefp) DnsQuestion *nq_idna = NULL, *nq_utf8 = NULL;
+ int r, k;
assert(q);
+ q->n_cname_redirects ++;
if (q->n_cname_redirects > CNAME_MAX)
return -ELOOP;
- r = dns_question_cname_redirect(q->question, cname, &nq);
+ r = dns_question_cname_redirect(q->question_idna, cname, &nq_idna);
if (r < 0)
return r;
+ else if (r > 0)
+ log_debug("Following CNAME/DNAME %s → %s.", dns_question_first_name(q->question_idna), dns_question_first_name(nq_idna));
- dns_question_unref(q->question);
- q->question = nq;
- nq = NULL;
+ k = dns_question_is_equal(q->question_idna, q->question_utf8);
+ if (k < 0)
+ return r;
+ if (k > 0) {
+ /* Same question? Shortcut new question generation */
+ nq_utf8 = dns_question_ref(nq_idna);
+ k = r;
+ } else {
+ k = dns_question_cname_redirect(q->question_utf8, cname, &nq_utf8);
+ if (k < 0)
+ return k;
+ else if (k > 0)
+ log_debug("Following UTF8 CNAME/DNAME %s → %s.", dns_question_first_name(q->question_utf8), dns_question_first_name(nq_utf8));
+ }
- q->n_cname_redirects++;
+ if (r == 0 && k == 0) /* No actual cname happened? */
+ return -ELOOP;
- dns_query_stop(q);
+ dns_question_unref(q->question_idna);
+ q->question_idna = nq_idna;
+ nq_idna = NULL;
+
+ dns_question_unref(q->question_utf8);
+ q->question_utf8 = nq_utf8;
+ nq_utf8 = NULL;
+
+ dns_query_free_candidates(q);
+ dns_query_reset_answer(q);
q->state = DNS_TRANSACTION_NULL;
+ /* Turn off searching for the new name */
+ q->flags |= SD_RESOLVED_NO_SEARCH;
+
return 0;
}
+int dns_query_process_cname(DnsQuery *q) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *cname = NULL;
+ DnsQuestion *question;
+ DnsResourceRecord *rr;
+ int r;
+
+ assert(q);
+
+ if (!IN_SET(q->state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_NULL))
+ return DNS_QUERY_NOMATCH;
+
+ question = dns_query_question_for_protocol(q, q->answer_protocol);
+
+ DNS_ANSWER_FOREACH(rr, q->answer) {
+ r = dns_question_matches_rr(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return DNS_QUERY_MATCH; /* The answer matches directly, no need to follow cnames */
+
+ r = dns_question_matches_cname_or_dname(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
+ if (r < 0)
+ return r;
+ if (r > 0 && !cname)
+ cname = dns_resource_record_ref(rr);
+ }
+
+ if (!cname)
+ return DNS_QUERY_NOMATCH; /* No match and no cname to follow */
+
+ if (q->flags & SD_RESOLVED_NO_CNAME)
+ return -ELOOP;
+
+ /* OK, let's actually follow the CNAME */
+ r = dns_query_cname_redirect(q, cname);
+ if (r < 0)
+ return r;
+
+ /* Let's see if the answer can already answer the new
+ * redirected question */
+ r = dns_query_process_cname(q);
+ if (r != DNS_QUERY_NOMATCH)
+ return r;
+
+ /* OK, it cannot, let's begin with the new query */
+ r = dns_query_go(q);
+ if (r < 0)
+ return r;
+
+ return DNS_QUERY_RESTARTED; /* We restarted the query for a new cname */
+}
+
static int on_bus_track(sd_bus_track *t, void *userdata) {
DnsQuery *q = userdata;
@@ -885,3 +1329,42 @@ int dns_query_bus_track(DnsQuery *q, sd_bus_message *m) {
return 0;
}
+
+DnsQuestion* dns_query_question_for_protocol(DnsQuery *q, DnsProtocol protocol) {
+ assert(q);
+
+ switch (protocol) {
+
+ case DNS_PROTOCOL_DNS:
+ return q->question_idna;
+
+ case DNS_PROTOCOL_MDNS:
+ case DNS_PROTOCOL_LLMNR:
+ return q->question_utf8;
+
+ default:
+ return NULL;
+ }
+}
+
+const char *dns_query_string(DnsQuery *q) {
+ const char *name;
+ int r;
+
+ /* Returns a somewhat useful human-readable lookup key string for this query */
+
+ if (q->request_address_string)
+ return q->request_address_string;
+
+ if (q->request_address_valid) {
+ r = in_addr_to_string(q->request_family, &q->request_address, &q->request_address_string);
+ if (r >= 0)
+ return q->request_address_string;
+ }
+
+ name = dns_question_first_name(q->question_utf8);
+ if (name)
+ return name;
+
+ return dns_question_first_name(q->question_idna);
+}
diff --git a/src/resolve/resolved-dns-query.h b/src/resolve/resolved-dns-query.h
index e7063d9678..9f618d6f6b 100644
--- a/src/resolve/resolved-dns-query.h
+++ b/src/resolve/resolved-dns-query.h
@@ -23,17 +23,48 @@
#include "sd-bus.h"
+
#include "set.h"
+typedef struct DnsQueryCandidate DnsQueryCandidate;
typedef struct DnsQuery DnsQuery;
-#include "resolved-dns-question.h"
#include "resolved-dns-answer.h"
+#include "resolved-dns-question.h"
#include "resolved-dns-stream.h"
+#include "resolved-dns-search-domain.h"
+
+struct DnsQueryCandidate {
+ DnsQuery *query;
+ DnsScope *scope;
+
+ DnsSearchDomain *search_domain;
+
+ int error_code;
+ Set *transactions;
+
+ LIST_FIELDS(DnsQueryCandidate, candidates_by_query);
+ LIST_FIELDS(DnsQueryCandidate, candidates_by_scope);
+};
struct DnsQuery {
Manager *manager;
- DnsQuestion *question;
+
+ /* When resolving a service, we first create a TXT+SRV query,
+ * and then for the hostnames we discover auxiliary A+AAAA
+ * queries. This pointer always points from the auxiliary
+ * queries back to the TXT+SRV query. */
+ DnsQuery *auxiliary_for;
+ LIST_HEAD(DnsQuery, auxiliary_queries);
+ unsigned n_auxiliary_queries;
+ int auxiliary_result;
+
+ /* The question, formatted in IDNA for use on classic DNS, and as UTF8 for use in LLMNR or mDNS. Note that even
+ * on classic DNS some labels might use UTF8 encoding. Specifically, DNS-SD service names (in contrast to their
+ * domain suffixes) use UTF-8 encoding even on DNS. Thus, the difference between these two fields is mostly
+ * relevant only for explicit *hostname* lookups as well as the domain suffixes of service lookups. */
+ DnsQuestion *question_idna;
+ DnsQuestion *question_utf8;
uint64_t flags;
int ifindex;
@@ -41,39 +72,59 @@ struct DnsQuery {
DnsTransactionState state;
unsigned n_cname_redirects;
+ LIST_HEAD(DnsQueryCandidate, candidates);
sd_event_source *timeout_event_source;
/* Discovered data */
DnsAnswer *answer;
- int answer_family;
- DnsProtocol answer_protocol;
int answer_rcode;
+ DnssecResult answer_dnssec_result;
+ bool answer_authenticated;
+ DnsProtocol answer_protocol;
+ int answer_family;
+ DnsSearchDomain *answer_search_domain;
/* Bus client information */
sd_bus_message *request;
int request_family;
- const char *request_hostname;
+ bool request_address_valid;
union in_addr_union request_address;
+ unsigned block_all_complete;
+ char *request_address_string;
/* Completion callback */
void (*complete)(DnsQuery* q);
unsigned block_ready;
- Set *transactions;
-
sd_bus_track *bus_track;
LIST_FIELDS(DnsQuery, queries);
+ LIST_FIELDS(DnsQuery, auxiliary_queries);
};
-int dns_query_new(Manager *m, DnsQuery **q, DnsQuestion *question, int family, uint64_t flags);
+enum {
+ DNS_QUERY_MATCH,
+ DNS_QUERY_NOMATCH,
+ DNS_QUERY_RESTARTED,
+};
+
+DnsQueryCandidate* dns_query_candidate_free(DnsQueryCandidate *c);
+void dns_query_candidate_notify(DnsQueryCandidate *c);
+
+int dns_query_new(Manager *m, DnsQuery **q, DnsQuestion *question_utf8, DnsQuestion *question_idna, int family, uint64_t flags);
DnsQuery *dns_query_free(DnsQuery *q);
+int dns_query_make_auxiliary(DnsQuery *q, DnsQuery *auxiliary_for);
+
int dns_query_go(DnsQuery *q);
void dns_query_ready(DnsQuery *q);
-int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname);
+int dns_query_process_cname(DnsQuery *q);
int dns_query_bus_track(DnsQuery *q, sd_bus_message *m);
+DnsQuestion* dns_query_question_for_protocol(DnsQuery *q, DnsProtocol protocol);
+
+const char *dns_query_string(DnsQuery *q);
+
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQuery*, dns_query_free);
diff --git a/src/resolve/resolved-dns-question.c b/src/resolve/resolved-dns-question.c
index 1507f22da0..1e41a9aa3c 100644
--- a/src/resolve/resolved-dns-question.c
+++ b/src/resolve/resolved-dns-question.c
@@ -19,8 +19,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "resolved-dns-question.h"
+#include "alloc-util.h"
#include "dns-domain.h"
+#include "dns-type.h"
+#include "resolved-dns-question.h"
DnsQuestion *dns_question_new(unsigned n) {
DnsQuestion *q;
@@ -88,7 +90,7 @@ int dns_question_add(DnsQuestion *q, DnsResourceKey *key) {
return 0;
}
-int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr) {
+int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) {
unsigned i;
int r;
@@ -98,7 +100,7 @@ int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr) {
return 0;
for (i = 0; i < q->n_keys; i++) {
- r = dns_resource_key_match_rr(q->keys[i], rr);
+ r = dns_resource_key_match_rr(q->keys[i], rr, search_domain);
if (r != 0)
return r;
}
@@ -106,7 +108,7 @@ int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr) {
return 0;
}
-int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr) {
+int dns_question_matches_cname_or_dname(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) {
unsigned i;
int r;
@@ -115,8 +117,15 @@ int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr) {
if (!q)
return 0;
+ if (!IN_SET(rr->key->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME))
+ return 0;
+
for (i = 0; i < q->n_keys; i++) {
- r = dns_resource_key_match_cname(q->keys[i], rr);
+ /* For a {C,D}NAME record we can never find a matching {C,D}NAME record */
+ if (!dns_type_may_redirect(q->keys[i]->type))
+ return 0;
+
+ r = dns_resource_key_match_cname_or_dname(q->keys[i], rr->key, search_domain);
if (r != 0)
return r;
}
@@ -124,7 +133,7 @@ int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr) {
return 0;
}
-int dns_question_is_valid(DnsQuestion *q) {
+int dns_question_is_valid_for_query(DnsQuestion *q) {
const char *name;
unsigned i;
int r;
@@ -143,62 +152,23 @@ int dns_question_is_valid(DnsQuestion *q) {
return 0;
/* Check that all keys in this question bear the same name */
- for (i = 1; i < q->n_keys; i++) {
+ for (i = 0; i < q->n_keys; i++) {
assert(q->keys[i]);
- r = dns_name_equal(DNS_RESOURCE_KEY_NAME(q->keys[i]), name);
- if (r <= 0)
- return r;
- }
-
- return 1;
-}
-
-int dns_question_is_superset(DnsQuestion *q, DnsQuestion *other) {
- unsigned j;
- int r;
-
- /* Checks if all keys in "other" are also contained in "q" */
-
- if (!other)
- return 1;
-
- for (j = 0; j < other->n_keys; j++) {
- DnsResourceKey *b = other->keys[j];
- bool found = false;
- unsigned i;
-
- if (!q)
- return 0;
-
- for (i = 0; i < q->n_keys; i++) {
- DnsResourceKey *a = q->keys[i];
-
- r = dns_name_equal(DNS_RESOURCE_KEY_NAME(a), DNS_RESOURCE_KEY_NAME(b));
- if (r < 0)
+ if (i > 0) {
+ r = dns_name_equal(DNS_RESOURCE_KEY_NAME(q->keys[i]), name);
+ if (r <= 0)
return r;
-
- if (r == 0)
- continue;
-
- if (a->class != b->class && a->class != DNS_CLASS_ANY)
- continue;
-
- if (a->type != b->type && a->type != DNS_TYPE_ANY)
- continue;
-
- found = true;
- break;
}
- if (!found)
+ if (!dns_type_is_valid_query(q->keys[i]->type))
return 0;
}
return 1;
}
-int dns_question_contains(DnsQuestion *a, DnsResourceKey *k) {
+int dns_question_contains(DnsQuestion *a, const DnsResourceKey *k) {
unsigned j;
int r;
@@ -220,6 +190,9 @@ int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b) {
unsigned j;
int r;
+ if (a == b)
+ return 1;
+
if (!a)
return !b || b->n_keys == 0;
if (!b)
@@ -244,25 +217,36 @@ int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b) {
int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, DnsQuestion **ret) {
_cleanup_(dns_question_unrefp) DnsQuestion *n = NULL;
+ DnsResourceKey *key;
bool same = true;
- unsigned i;
int r;
assert(cname);
assert(ret);
+ assert(IN_SET(cname->key->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME));
- if (!q) {
- n = dns_question_new(0);
- if (!n)
- return -ENOMEM;
-
- *ret = n;
- n = 0;
+ if (dns_question_size(q) <= 0) {
+ *ret = NULL;
return 0;
}
- for (i = 0; i < q->n_keys; i++) {
- r = dns_name_equal(DNS_RESOURCE_KEY_NAME(q->keys[i]), cname->cname.name);
+ DNS_QUESTION_FOREACH(key, q) {
+ _cleanup_free_ char *destination = NULL;
+ const char *d;
+
+ if (cname->key->type == DNS_TYPE_CNAME)
+ d = cname->cname.name;
+ else {
+ r = dns_name_change_suffix(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(cname->key), cname->dname.name, &destination);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ d = destination;
+ }
+
+ r = dns_name_equal(DNS_RESOURCE_KEY_NAME(key), d);
if (r < 0)
return r;
@@ -272,9 +256,9 @@ int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname,
}
}
+ /* Fully the same, indicate we didn't do a thing */
if (same) {
- /* Shortcut, the names are already right */
- *ret = dns_question_ref(q);
+ *ret = NULL;
return 0;
}
@@ -283,10 +267,10 @@ int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname,
return -ENOMEM;
/* Create a new question, and patch in the new name */
- for (i = 0; i < q->n_keys; i++) {
+ DNS_QUESTION_FOREACH(key, q) {
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *k = NULL;
- k = dns_resource_key_new_redirect(q->keys[i], cname);
+ k = dns_resource_key_new_redirect(key, cname);
if (!k)
return -ENOMEM;
@@ -300,3 +284,187 @@ int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname,
return 1;
}
+
+const char *dns_question_first_name(DnsQuestion *q) {
+
+ if (!q)
+ return NULL;
+
+ if (q->n_keys < 1)
+ return NULL;
+
+ return DNS_RESOURCE_KEY_NAME(q->keys[0]);
+}
+
+int dns_question_new_address(DnsQuestion **ret, int family, const char *name, bool convert_idna) {
+ _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL;
+ _cleanup_free_ char *buf = NULL;
+ int r;
+
+ assert(ret);
+ assert(name);
+
+ if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
+ return -EAFNOSUPPORT;
+
+ if (convert_idna) {
+ r = dns_name_apply_idna(name, &buf);
+ if (r < 0)
+ return r;
+
+ name = buf;
+ }
+
+ q = dns_question_new(family == AF_UNSPEC ? 2 : 1);
+ if (!q)
+ return -ENOMEM;
+
+ if (family != AF_INET6) {
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
+
+ key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, name);
+ if (!key)
+ return -ENOMEM;
+
+ r = dns_question_add(q, key);
+ if (r < 0)
+ return r;
+ }
+
+ if (family != AF_INET) {
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
+
+ key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, name);
+ if (!key)
+ return -ENOMEM;
+
+ r = dns_question_add(q, key);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = q;
+ q = NULL;
+
+ return 0;
+}
+
+int dns_question_new_reverse(DnsQuestion **ret, int family, const union in_addr_union *a) {
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
+ _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL;
+ _cleanup_free_ char *reverse = NULL;
+ int r;
+
+ assert(ret);
+ assert(a);
+
+ if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
+ return -EAFNOSUPPORT;
+
+ r = dns_name_reverse(family, a, &reverse);
+ if (r < 0)
+ return r;
+
+ q = dns_question_new(1);
+ if (!q)
+ return -ENOMEM;
+
+ key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, reverse);
+ if (!key)
+ return -ENOMEM;
+
+ reverse = NULL;
+
+ r = dns_question_add(q, key);
+ if (r < 0)
+ return r;
+
+ *ret = q;
+ q = NULL;
+
+ return 0;
+}
+
+int dns_question_new_service(
+ DnsQuestion **ret,
+ const char *service,
+ const char *type,
+ const char *domain,
+ bool with_txt,
+ bool convert_idna) {
+
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
+ _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL;
+ _cleanup_free_ char *buf = NULL, *joined = NULL;
+ const char *name;
+ int r;
+
+ assert(ret);
+
+ /* We support three modes of invocation:
+ *
+ * 1. Only a domain is specified, in which case we assume a properly encoded SRV RR name, including service
+ * type and possibly a service name. If specified in this way we assume it's already IDNA converted if
+ * that's necessary.
+ *
+ * 2. Both service type and a domain specified, in which case a normal SRV RR is assumed, without a DNS-SD
+ * style prefix. In this case we'll IDNA convert the domain, if that's requested.
+ *
+ * 3. All three of service name, type and domain are specified, in which case a DNS-SD service is put
+ * together. The service name is never IDNA converted, and the domain is if requested.
+ *
+ * It's not supported to specify a service name without a type, or no domain name.
+ */
+
+ if (!domain)
+ return -EINVAL;
+
+ if (type) {
+ if (convert_idna) {
+ r = dns_name_apply_idna(domain, &buf);
+ if (r < 0)
+ return r;
+
+ domain = buf;
+ }
+
+ r = dns_service_join(service, type, domain, &joined);
+ if (r < 0)
+ return r;
+
+ name = joined;
+ } else {
+ if (service)
+ return -EINVAL;
+
+ name = domain;
+ }
+
+ q = dns_question_new(1 + with_txt);
+ if (!q)
+ return -ENOMEM;
+
+ key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_SRV, name);
+ if (!key)
+ return -ENOMEM;
+
+ r = dns_question_add(q, key);
+ if (r < 0)
+ return r;
+
+ if (with_txt) {
+ dns_resource_key_unref(key);
+ key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_TXT, name);
+ if (!key)
+ return -ENOMEM;
+
+ r = dns_question_add(q, key);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = q;
+ q = NULL;
+
+ return 0;
+}
diff --git a/src/resolve/resolved-dns-question.h b/src/resolve/resolved-dns-question.h
index 13cd1f20f3..98e1f0e366 100644
--- a/src/resolve/resolved-dns-question.h
+++ b/src/resolve/resolved-dns-question.h
@@ -23,9 +23,10 @@
typedef struct DnsQuestion DnsQuestion;
+#include "macro.h"
#include "resolved-dns-rr.h"
-/* A simple array of resources keys */
+/* A simple array of resource keys */
struct DnsQuestion {
unsigned n_ref;
@@ -37,15 +38,34 @@ DnsQuestion *dns_question_new(unsigned n);
DnsQuestion *dns_question_ref(DnsQuestion *q);
DnsQuestion *dns_question_unref(DnsQuestion *q);
+int dns_question_new_address(DnsQuestion **ret, int family, const char *name, bool convert_idna);
+int dns_question_new_reverse(DnsQuestion **ret, int family, const union in_addr_union *a);
+int dns_question_new_service(DnsQuestion **ret, const char *service, const char *type, const char *domain, bool with_txt, bool convert_idna);
+
int dns_question_add(DnsQuestion *q, DnsResourceKey *key);
-int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr);
-int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr);
-int dns_question_is_valid(DnsQuestion *q);
-int dns_question_is_superset(DnsQuestion *q, DnsQuestion *other);
-int dns_question_contains(DnsQuestion *a, DnsResourceKey *k);
+int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain);
+int dns_question_matches_cname_or_dname(DnsQuestion *q, DnsResourceRecord *rr, const char* search_domain);
+int dns_question_is_valid_for_query(DnsQuestion *q);
+int dns_question_contains(DnsQuestion *a, const DnsResourceKey *k);
int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b);
int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, DnsQuestion **ret);
+const char *dns_question_first_name(DnsQuestion *q);
+
+static inline unsigned dns_question_size(DnsQuestion *q) {
+ return q ? q->n_keys : 0;
+}
+
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQuestion*, dns_question_unref);
+
+#define _DNS_QUESTION_FOREACH(u, key, q) \
+ for (unsigned UNIQ_T(i, u) = ({ \
+ (key) = ((q) && (q)->n_keys > 0) ? (q)->keys[0] : NULL; \
+ 0; \
+ }); \
+ (q) && (UNIQ_T(i, u) < (q)->n_keys); \
+ UNIQ_T(i, u)++, (key) = (UNIQ_T(i, u) < (q)->n_keys ? (q)->keys[UNIQ_T(i, u)] : NULL))
+
+#define DNS_QUESTION_FOREACH(key, q) _DNS_QUESTION_FOREACH(UNIQ, key, q)
diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c
index fd2f53f40b..7273ef3825 100644
--- a/src/resolve/resolved-dns-rr.c
+++ b/src/resolve/resolved-dns-rr.c
@@ -21,12 +21,15 @@
#include <math.h>
-#include "strv.h"
-
+#include "alloc-util.h"
#include "dns-domain.h"
-#include "resolved-dns-rr.h"
-#include "resolved-dns-packet.h"
#include "dns-type.h"
+#include "hexdecoct.h"
+#include "resolved-dns-packet.h"
+#include "resolved-dns-rr.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "strv.h"
DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name) {
DnsResourceKey *k;
@@ -48,17 +51,62 @@ DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *
return k;
}
-DnsResourceKey* dns_resource_key_new_cname(const DnsResourceKey *key) {
+DnsResourceKey* dns_resource_key_new_redirect(const DnsResourceKey *key, const DnsResourceRecord *cname) {
+ int r;
+
assert(key);
+ assert(cname);
+
+ assert(IN_SET(cname->key->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME));
+
+ if (cname->key->type == DNS_TYPE_CNAME)
+ return dns_resource_key_new(key->class, key->type, cname->cname.name);
+ else {
+ DnsResourceKey *k;
+ char *destination = NULL;
- return dns_resource_key_new(key->class, DNS_TYPE_CNAME, DNS_RESOURCE_KEY_NAME(key));
+ r = dns_name_change_suffix(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(cname->key), cname->dname.name, &destination);
+ if (r < 0)
+ return NULL;
+ if (r == 0)
+ return dns_resource_key_ref((DnsResourceKey*) key);
+
+ k = dns_resource_key_new_consume(key->class, key->type, destination);
+ if (!k) {
+ free(destination);
+ return NULL;
+ }
+
+ return k;
+ }
}
-DnsResourceKey* dns_resource_key_new_redirect(const DnsResourceKey *key, const DnsResourceRecord *cname) {
+int dns_resource_key_new_append_suffix(DnsResourceKey **ret, DnsResourceKey *key, char *name) {
+ DnsResourceKey *new_key;
+ char *joined;
+ int r;
+
+ assert(ret);
assert(key);
- assert(cname);
+ assert(name);
+
+ if (dns_name_is_root(name)) {
+ *ret = dns_resource_key_ref(key);
+ return 0;
+ }
+
+ r = dns_name_concat(DNS_RESOURCE_KEY_NAME(key), name, &joined);
+ if (r < 0)
+ return r;
- return dns_resource_key_new(key->class, key->type, cname->cname.name);
+ new_key = dns_resource_key_new_consume(key->class, key->type, joined);
+ if (!new_key) {
+ free(joined);
+ return -ENOMEM;
+ }
+
+ *ret = new_key;
+ return 0;
}
DnsResourceKey* dns_resource_key_new_consume(uint16_t class, uint16_t type, char *name) {
@@ -83,6 +131,10 @@ DnsResourceKey* dns_resource_key_ref(DnsResourceKey *k) {
if (!k)
return NULL;
+ /* Static/const keys created with DNS_RESOURCE_KEY_CONST will
+ * set this to -1, they should not be reffed/unreffed */
+ assert(k->n_ref != (unsigned) -1);
+
assert(k->n_ref > 0);
k->n_ref++;
@@ -93,6 +145,7 @@ DnsResourceKey* dns_resource_key_unref(DnsResourceKey *k) {
if (!k)
return NULL;
+ assert(k->n_ref != (unsigned) -1);
assert(k->n_ref > 0);
if (k->n_ref == 1) {
@@ -104,9 +157,20 @@ DnsResourceKey* dns_resource_key_unref(DnsResourceKey *k) {
return NULL;
}
+bool dns_resource_key_is_address(const DnsResourceKey *key) {
+ assert(key);
+
+ /* Check if this is an A or AAAA resource key */
+
+ return key->class == DNS_CLASS_IN && IN_SET(key->type, DNS_TYPE_A, DNS_TYPE_AAAA);
+}
+
int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b) {
int r;
+ if (a == b)
+ return 1;
+
r = dns_name_equal(DNS_RESOURCE_KEY_NAME(a), DNS_RESOURCE_KEY_NAME(b));
if (r <= 0)
return r;
@@ -120,41 +184,100 @@ int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b) {
return 1;
}
-int dns_resource_key_match_rr(const DnsResourceKey *key, const DnsResourceRecord *rr) {
+int dns_resource_key_match_rr(const DnsResourceKey *key, DnsResourceRecord *rr, const char *search_domain) {
+ int r;
+
assert(key);
assert(rr);
+ if (key == rr->key)
+ return 1;
+
+ /* Checks if an rr matches the specified key. If a search
+ * domain is specified, it will also be checked if the key
+ * with the search domain suffixed might match the RR. */
+
if (rr->key->class != key->class && key->class != DNS_CLASS_ANY)
return 0;
if (rr->key->type != key->type && key->type != DNS_TYPE_ANY)
return 0;
- return dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
+ r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
+ if (r != 0)
+ return r;
+
+ if (search_domain) {
+ _cleanup_free_ char *joined = NULL;
+
+ r = dns_name_concat(DNS_RESOURCE_KEY_NAME(key), search_domain, &joined);
+ if (r < 0)
+ return r;
+
+ return dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), joined);
+ }
+
+ return 0;
}
-int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRecord *rr) {
+int dns_resource_key_match_cname_or_dname(const DnsResourceKey *key, const DnsResourceKey *cname, const char *search_domain) {
+ int r;
+
assert(key);
- assert(rr);
+ assert(cname);
- if (rr->key->class != key->class && key->class != DNS_CLASS_ANY)
+ if (cname->class != key->class && key->class != DNS_CLASS_ANY)
return 0;
- if (rr->key->type != DNS_TYPE_CNAME)
+ if (cname->type == DNS_TYPE_CNAME)
+ r = dns_name_equal(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(cname));
+ else if (cname->type == DNS_TYPE_DNAME)
+ r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(cname));
+ else
return 0;
- return dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
+ if (r != 0)
+ return r;
+
+ if (search_domain) {
+ _cleanup_free_ char *joined = NULL;
+
+ r = dns_name_concat(DNS_RESOURCE_KEY_NAME(key), search_domain, &joined);
+ if (r < 0)
+ return r;
+
+ if (cname->type == DNS_TYPE_CNAME)
+ return dns_name_equal(joined, DNS_RESOURCE_KEY_NAME(cname));
+ else if (cname->type == DNS_TYPE_DNAME)
+ return dns_name_endswith(joined, DNS_RESOURCE_KEY_NAME(cname));
+ }
+
+ return 0;
}
-static unsigned long dns_resource_key_hash_func(const void *i, const uint8_t hash_key[HASH_KEY_SIZE]) {
+int dns_resource_key_match_soa(const DnsResourceKey *key, const DnsResourceKey *soa) {
+ assert(soa);
+ assert(key);
+
+ /* Checks whether 'soa' is a SOA record for the specified key. */
+
+ if (soa->class != key->class)
+ return 0;
+
+ if (soa->type != DNS_TYPE_SOA)
+ return 0;
+
+ return dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(soa));
+}
+
+static void dns_resource_key_hash_func(const void *i, struct siphash *state) {
const DnsResourceKey *k = i;
- unsigned long ul;
- ul = dns_name_hash_func(DNS_RESOURCE_KEY_NAME(k), hash_key);
- ul = ul * hash_key[0] + ul + k->class;
- ul = ul * hash_key[1] + ul + k->type;
+ assert(k);
- return ul;
+ dns_name_hash_func(DNS_RESOURCE_KEY_NAME(k), state);
+ siphash24_compress(&k->class, sizeof(k->class), state);
+ siphash24_compress(&k->type, sizeof(k->type), state);
}
static int dns_resource_key_compare_func(const void *a, const void *b) {
@@ -185,9 +308,12 @@ const struct hash_ops dns_resource_key_hash_ops = {
int dns_resource_key_to_string(const DnsResourceKey *key, char **ret) {
char cbuf[strlen("CLASS") + DECIMAL_STR_MAX(uint16_t)], tbuf[strlen("TYPE") + DECIMAL_STR_MAX(uint16_t)];
- const char *c, *t;
+ const char *c, *t, *n;
char *s;
+ /* If we cannot convert the CLASS/TYPE into a known string,
+ use the format recommended by RFC 3597, Section 5. */
+
c = dns_class_to_string(key->class);
if (!c) {
sprintf(cbuf, "CLASS%u", key->class);
@@ -200,13 +326,54 @@ int dns_resource_key_to_string(const DnsResourceKey *key, char **ret) {
t = tbuf;
}
- if (asprintf(&s, "%s %s %-5s", DNS_RESOURCE_KEY_NAME(key), c, t) < 0)
+ n = DNS_RESOURCE_KEY_NAME(key);
+ if (asprintf(&s, "%s%s %s %-5s", n, endswith(n, ".") ? "" : ".", c, t) < 0)
return -ENOMEM;
*ret = s;
return 0;
}
+bool dns_resource_key_reduce(DnsResourceKey **a, DnsResourceKey **b) {
+ assert(a);
+ assert(b);
+
+ /* Try to replace one RR key by another if they are identical, thus saving a bit of memory. Note that we do
+ * this only for RR keys, not for RRs themselves, as they carry a lot of additional metadata (where they come
+ * from, validity data, and suchlike), and cannot be replaced so easily by other RRs that have the same
+ * superficial data. */
+
+ if (!*a)
+ return false;
+ if (!*b)
+ return false;
+
+ /* We refuse merging const keys */
+ if ((*a)->n_ref == (unsigned) -1)
+ return false;
+ if ((*b)->n_ref == (unsigned) -1)
+ return false;
+
+ /* Already the same? */
+ if (*a == *b)
+ return true;
+
+ /* Are they really identical? */
+ if (dns_resource_key_equal(*a, *b) <= 0)
+ return false;
+
+ /* Keep the one which already has more references. */
+ if ((*a)->n_ref > (*b)->n_ref) {
+ dns_resource_key_unref(*b);
+ *b = dns_resource_key_ref(*a);
+ } else {
+ dns_resource_key_unref(*a);
+ *a = dns_resource_key_ref(*b);
+ }
+
+ return true;
+}
+
DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key) {
DnsResourceRecord *rr;
@@ -216,6 +383,8 @@ DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key) {
rr->n_ref = 1;
rr->key = dns_resource_key_ref(key);
+ rr->expiry = USEC_INFINITY;
+ rr->n_skip_labels_signer = rr->n_skip_labels_source = (unsigned) -1;
return rr;
}
@@ -272,7 +441,7 @@ DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) {
case DNS_TYPE_TXT:
case DNS_TYPE_SPF:
- strv_free(rr->txt.strings);
+ dns_txt_item_free_all(rr->txt.items);
break;
case DNS_TYPE_SOA:
@@ -321,9 +490,11 @@ DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) {
free(rr->generic.data);
}
+ free(rr->wire_format);
dns_resource_key_unref(rr->key);
}
+ free(rr->to_string);
free(rr);
return NULL;
@@ -399,6 +570,9 @@ int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecor
assert(a);
assert(b);
+ if (a == b)
+ return 1;
+
r = dns_resource_key_equal(a->key, b->key);
if (r <= 0)
return r;
@@ -429,7 +603,7 @@ int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecor
case DNS_TYPE_SPF: /* exactly the same as TXT */
case DNS_TYPE_TXT:
- return strv_equal(a->txt.strings, b->txt.strings);
+ return dns_txt_item_equal(a->txt.items, b->txt.items);
case DNS_TYPE_A:
return memcmp(&a->a.in_addr, &b->a.in_addr, sizeof(struct in_addr)) == 0;
@@ -481,8 +655,8 @@ int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecor
memcmp(a->sshfp.fingerprint, b->sshfp.fingerprint, a->sshfp.fingerprint_size) == 0;
case DNS_TYPE_DNSKEY:
- return a->dnskey.zone_key_flag == b->dnskey.zone_key_flag &&
- a->dnskey.sep_flag == b->dnskey.sep_flag &&
+ return a->dnskey.flags == b->dnskey.flags &&
+ a->dnskey.protocol == b->dnskey.protocol &&
a->dnskey.algorithm == b->dnskey.algorithm &&
a->dnskey.key_size == b->dnskey.key_size &&
memcmp(a->dnskey.key, b->dnskey.key, a->dnskey.key_size) == 0;
@@ -599,16 +773,56 @@ static char *format_types(Bitmap *types) {
return strjoin("( ", str, " )", NULL);
}
-int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
+static char *format_txt(DnsTxtItem *first) {
+ DnsTxtItem *i;
+ size_t c = 1;
+ char *p, *s;
+
+ LIST_FOREACH(items, i, first)
+ c += i->length * 4 + 3;
+
+ p = s = new(char, c);
+ if (!s)
+ return NULL;
+
+ LIST_FOREACH(items, i, first) {
+ size_t j;
+
+ if (i != first)
+ *(p++) = ' ';
+
+ *(p++) = '"';
+
+ for (j = 0; j < i->length; j++) {
+ if (i->data[j] < ' ' || i->data[j] == '"' || i->data[j] >= 127) {
+ *(p++) = '\\';
+ *(p++) = '0' + (i->data[j] / 100);
+ *(p++) = '0' + ((i->data[j] / 10) % 10);
+ *(p++) = '0' + (i->data[j] % 10);
+ } else
+ *(p++) = i->data[j];
+ }
+
+ *(p++) = '"';
+ }
+
+ *p = 0;
+ return s;
+}
+
+const char *dns_resource_record_to_string(DnsResourceRecord *rr) {
_cleanup_free_ char *k = NULL, *t = NULL;
char *s;
int r;
assert(rr);
+ if (rr->to_string)
+ return rr->to_string;
+
r = dns_resource_key_to_string(rr->key, &k);
if (r < 0)
- return r;
+ return NULL;
switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
@@ -620,7 +834,7 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
rr->srv.port,
strna(rr->srv.name));
if (r < 0)
- return -ENOMEM;
+ return NULL;
break;
case DNS_TYPE_PTR:
@@ -629,26 +843,25 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
case DNS_TYPE_DNAME:
s = strjoin(k, " ", rr->ptr.name, NULL);
if (!s)
- return -ENOMEM;
+ return NULL;
break;
case DNS_TYPE_HINFO:
s = strjoin(k, " ", rr->hinfo.cpu, " ", rr->hinfo.os, NULL);
if (!s)
- return -ENOMEM;
+ return NULL;
break;
case DNS_TYPE_SPF: /* exactly the same as TXT */
case DNS_TYPE_TXT:
- t = strv_join_quoted(rr->txt.strings);
+ t = format_txt(rr->txt.items);
if (!t)
- return -ENOMEM;
+ return NULL;
s = strjoin(k, " ", t, NULL);
if (!s)
- return -ENOMEM;
-
+ return NULL;
break;
case DNS_TYPE_A: {
@@ -656,22 +869,22 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
r = in_addr_to_string(AF_INET, (const union in_addr_union*) &rr->a.in_addr, &x);
if (r < 0)
- return r;
+ return NULL;
s = strjoin(k, " ", x, NULL);
if (!s)
- return -ENOMEM;
+ return NULL;
break;
}
case DNS_TYPE_AAAA:
r = in_addr_to_string(AF_INET6, (const union in_addr_union*) &rr->aaaa.in6_addr, &t);
if (r < 0)
- return r;
+ return NULL;
s = strjoin(k, " ", t, NULL);
if (!s)
- return -ENOMEM;
+ return NULL;
break;
case DNS_TYPE_SOA:
@@ -685,7 +898,7 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
rr->soa.expire,
rr->soa.minimum);
if (r < 0)
- return -ENOMEM;
+ return NULL;
break;
case DNS_TYPE_MX:
@@ -694,7 +907,7 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
rr->mx.priority,
rr->mx.exchange);
if (r < 0)
- return -ENOMEM;
+ return NULL;
break;
case DNS_TYPE_LOC:
@@ -707,17 +920,17 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
rr->loc.horiz_pre,
rr->loc.vert_pre);
if (!t)
- return -ENOMEM;
+ return NULL;
s = strjoin(k, " ", t, NULL);
if (!s)
- return -ENOMEM;
+ return NULL;
break;
case DNS_TYPE_DS:
t = hexmem(rr->ds.digest, rr->ds.digest_size);
if (!t)
- return -ENOMEM;
+ return NULL;
r = asprintf(&s, "%s %u %u %u %s",
k,
@@ -726,13 +939,13 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
rr->ds.digest_type,
t);
if (r < 0)
- return -ENOMEM;
+ return NULL;
break;
case DNS_TYPE_SSHFP:
t = hexmem(rr->sshfp.fingerprint, rr->sshfp.fingerprint_size);
if (!t)
- return -ENOMEM;
+ return NULL;
r = asprintf(&s, "%s %u %u %s",
k,
@@ -740,57 +953,62 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
rr->sshfp.fptype,
t);
if (r < 0)
- return -ENOMEM;
+ return NULL;
break;
case DNS_TYPE_DNSKEY: {
- const char *alg;
+ _cleanup_free_ char *alg = NULL;
- alg = dnssec_algorithm_to_string(rr->dnskey.algorithm);
+ r = dnssec_algorithm_to_string_alloc(rr->dnskey.algorithm, &alg);
+ if (r < 0)
+ return NULL;
t = base64mem(rr->dnskey.key, rr->dnskey.key_size);
if (!t)
- return -ENOMEM;
+ return NULL;
- r = asprintf(&s, "%s %u 3 %.*s%.*u %s",
+ r = asprintf(&s, "%s %u %u %s %s",
k,
- dnskey_to_flags(rr),
- alg ? -1 : 0, alg,
- alg ? 0 : 1, alg ? 0u : (unsigned) rr->dnskey.algorithm,
+ rr->dnskey.flags,
+ rr->dnskey.protocol,
+ alg,
t);
if (r < 0)
- return -ENOMEM;
+ return NULL;
break;
}
case DNS_TYPE_RRSIG: {
- const char *type, *alg;
+ _cleanup_free_ char *alg = NULL;
char expiration[strlen("YYYYMMDDHHmmSS") + 1], inception[strlen("YYYYMMDDHHmmSS") + 1];
+ const char *type;
type = dns_type_to_string(rr->rrsig.type_covered);
- alg = dnssec_algorithm_to_string(rr->rrsig.algorithm);
+
+ r = dnssec_algorithm_to_string_alloc(rr->rrsig.algorithm, &alg);
+ if (r < 0)
+ return NULL;
t = base64mem(rr->rrsig.signature, rr->rrsig.signature_size);
if (!t)
- return -ENOMEM;
+ return NULL;
r = format_timestamp_dns(expiration, sizeof(expiration), rr->rrsig.expiration);
if (r < 0)
- return r;
+ return NULL;
r = format_timestamp_dns(inception, sizeof(inception), rr->rrsig.inception);
if (r < 0)
- return r;
+ return NULL;
/* TYPE?? follows
* http://tools.ietf.org/html/rfc3597#section-5 */
- r = asprintf(&s, "%s %s%.*u %.*s%.*u %u %u %s %s %u %s %s",
+ r = asprintf(&s, "%s %s%.*u %s %u %u %s %s %u %s %s",
k,
type ?: "TYPE",
type ? 0 : 1, type ? 0u : (unsigned) rr->rrsig.type_covered,
- alg ? -1 : 0, alg,
- alg ? 0 : 1, alg ? 0u : (unsigned) rr->rrsig.algorithm,
+ alg,
rr->rrsig.labels,
rr->rrsig.original_ttl,
expiration,
@@ -799,21 +1017,21 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
rr->rrsig.signer,
t);
if (r < 0)
- return -ENOMEM;
+ return NULL;
break;
}
case DNS_TYPE_NSEC:
t = format_types(rr->nsec.types);
if (!t)
- return -ENOMEM;
+ return NULL;
r = asprintf(&s, "%s %s %s",
k,
rr->nsec.next_domain_name,
t);
if (r < 0)
- return -ENOMEM;
+ return NULL;
break;
case DNS_TYPE_NSEC3: {
@@ -822,16 +1040,16 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
if (rr->nsec3.salt_size > 0) {
salt = hexmem(rr->nsec3.salt, rr->nsec3.salt_size);
if (!salt)
- return -ENOMEM;
+ return NULL;
}
hash = base32hexmem(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, false);
if (!hash)
- return -ENOMEM;
+ return NULL;
t = format_types(rr->nsec3.types);
if (!t)
- return -ENOMEM;
+ return NULL;
r = asprintf(&s, "%s %"PRIu8" %"PRIu8" %"PRIu16" %s %s %s",
k,
@@ -842,7 +1060,7 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
hash,
t);
if (r < 0)
- return -ENOMEM;
+ return NULL;
break;
}
@@ -850,42 +1068,354 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
default:
t = hexmem(rr->generic.data, rr->generic.size);
if (!t)
- return -ENOMEM;
+ return NULL;
+ /* Format as documented in RFC 3597, Section 5 */
r = asprintf(&s, "%s \\# %zu %s", k, rr->generic.size, t);
if (r < 0)
- return -ENOMEM;
+ return NULL;
break;
}
- *ret = s;
+ rr->to_string = s;
+ return s;
+}
+
+int dns_resource_record_to_wire_format(DnsResourceRecord *rr, bool canonical) {
+
+ DnsPacket packet = {
+ .n_ref = 1,
+ .protocol = DNS_PROTOCOL_DNS,
+ .on_stack = true,
+ .refuse_compression = true,
+ .canonical_form = canonical,
+ };
+
+ size_t start, rds;
+ int r;
+
+ assert(rr);
+
+ /* Generates the RR in wire-format, optionally in the
+ * canonical form as discussed in the DNSSEC RFC 4034, Section
+ * 6.2. We allocate a throw-away DnsPacket object on the stack
+ * here, because we need some book-keeping for memory
+ * management, and can reuse the DnsPacket serializer, that
+ * can generate the canonical form, too, but also knows label
+ * compression and suchlike. */
+
+ if (rr->wire_format && rr->wire_format_canonical == canonical)
+ return 0;
+
+ r = dns_packet_append_rr(&packet, rr, &start, &rds);
+ if (r < 0)
+ return r;
+
+ assert(start == 0);
+ assert(packet._data);
+
+ free(rr->wire_format);
+ rr->wire_format = packet._data;
+ rr->wire_format_size = packet.size;
+ rr->wire_format_rdata_offset = rds;
+ rr->wire_format_canonical = canonical;
+
+ packet._data = NULL;
+ dns_packet_unref(&packet);
+
return 0;
}
-const char *dns_class_to_string(uint16_t class) {
+int dns_resource_record_signer(DnsResourceRecord *rr, const char **ret) {
+ const char *n;
+ int r;
- switch (class) {
+ assert(rr);
+ assert(ret);
- case DNS_CLASS_IN:
- return "IN";
+ /* Returns the RRset's signer, if it is known. */
- case DNS_CLASS_ANY:
- return "ANY";
- }
+ if (rr->n_skip_labels_signer == (unsigned) -1)
+ return -ENODATA;
- return NULL;
+ n = DNS_RESOURCE_KEY_NAME(rr->key);
+ r = dns_name_skip(n, rr->n_skip_labels_signer, &n);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EINVAL;
+
+ *ret = n;
+ return 0;
}
-int dns_class_from_string(const char *s, uint16_t *class) {
- assert(s);
- assert(class);
+int dns_resource_record_source(DnsResourceRecord *rr, const char **ret) {
+ const char *n;
+ int r;
- if (strcaseeq(s, "IN"))
- *class = DNS_CLASS_IN;
- else if (strcaseeq(s, "ANY"))
- *class = DNS_CLASS_ANY;
- else
+ assert(rr);
+ assert(ret);
+
+ /* Returns the RRset's synthesizing source, if it is known. */
+
+ if (rr->n_skip_labels_source == (unsigned) -1)
+ return -ENODATA;
+
+ n = DNS_RESOURCE_KEY_NAME(rr->key);
+ r = dns_name_skip(n, rr->n_skip_labels_source, &n);
+ if (r < 0)
+ return r;
+ if (r == 0)
return -EINVAL;
+ *ret = n;
return 0;
}
+
+int dns_resource_record_is_signer(DnsResourceRecord *rr, const char *zone) {
+ const char *signer;
+ int r;
+
+ assert(rr);
+
+ r = dns_resource_record_signer(rr, &signer);
+ if (r < 0)
+ return r;
+
+ return dns_name_equal(zone, signer);
+}
+
+int dns_resource_record_is_synthetic(DnsResourceRecord *rr) {
+ int r;
+
+ assert(rr);
+
+ /* Returns > 0 if the RR is generated from a wildcard, and is not the asterisk name itself */
+
+ if (rr->n_skip_labels_source == (unsigned) -1)
+ return -ENODATA;
+
+ if (rr->n_skip_labels_source == 0)
+ return 0;
+
+ if (rr->n_skip_labels_source > 1)
+ return 1;
+
+ r = dns_name_startswith(DNS_RESOURCE_KEY_NAME(rr->key), "*");
+ if (r < 0)
+ return r;
+
+ return !r;
+}
+
+static void dns_resource_record_hash_func(const void *i, struct siphash *state) {
+ const DnsResourceRecord *rr = i;
+
+ assert(rr);
+
+ dns_resource_key_hash_func(rr->key, state);
+
+ switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
+
+ case DNS_TYPE_SRV:
+ siphash24_compress(&rr->srv.priority, sizeof(rr->srv.priority), state);
+ siphash24_compress(&rr->srv.weight, sizeof(rr->srv.weight), state);
+ siphash24_compress(&rr->srv.port, sizeof(rr->srv.port), state);
+ dns_name_hash_func(rr->srv.name, state);
+ break;
+
+ case DNS_TYPE_PTR:
+ case DNS_TYPE_NS:
+ case DNS_TYPE_CNAME:
+ case DNS_TYPE_DNAME:
+ dns_name_hash_func(rr->ptr.name, state);
+ break;
+
+ case DNS_TYPE_HINFO:
+ string_hash_func(rr->hinfo.cpu, state);
+ string_hash_func(rr->hinfo.os, state);
+ break;
+
+ case DNS_TYPE_TXT:
+ case DNS_TYPE_SPF: {
+ DnsTxtItem *j;
+
+ LIST_FOREACH(items, j, rr->txt.items) {
+ siphash24_compress(j->data, j->length, state);
+
+ /* Add an extra NUL byte, so that "a" followed by "b" doesn't result in the same hash as "ab"
+ * followed by "". */
+ siphash24_compress_byte(0, state);
+ }
+ break;
+ }
+
+ case DNS_TYPE_A:
+ siphash24_compress(&rr->a.in_addr, sizeof(rr->a.in_addr), state);
+ break;
+
+ case DNS_TYPE_AAAA:
+ siphash24_compress(&rr->aaaa.in6_addr, sizeof(rr->aaaa.in6_addr), state);
+ break;
+
+ case DNS_TYPE_SOA:
+ dns_name_hash_func(rr->soa.mname, state);
+ dns_name_hash_func(rr->soa.rname, state);
+ siphash24_compress(&rr->soa.serial, sizeof(rr->soa.serial), state);
+ siphash24_compress(&rr->soa.refresh, sizeof(rr->soa.refresh), state);
+ siphash24_compress(&rr->soa.retry, sizeof(rr->soa.retry), state);
+ siphash24_compress(&rr->soa.expire, sizeof(rr->soa.expire), state);
+ siphash24_compress(&rr->soa.minimum, sizeof(rr->soa.minimum), state);
+ break;
+
+ case DNS_TYPE_MX:
+ siphash24_compress(&rr->mx.priority, sizeof(rr->mx.priority), state);
+ dns_name_hash_func(rr->mx.exchange, state);
+ break;
+
+ case DNS_TYPE_LOC:
+ siphash24_compress(&rr->loc.version, sizeof(rr->loc.version), state);
+ siphash24_compress(&rr->loc.size, sizeof(rr->loc.size), state);
+ siphash24_compress(&rr->loc.horiz_pre, sizeof(rr->loc.horiz_pre), state);
+ siphash24_compress(&rr->loc.vert_pre, sizeof(rr->loc.vert_pre), state);
+ siphash24_compress(&rr->loc.latitude, sizeof(rr->loc.latitude), state);
+ siphash24_compress(&rr->loc.longitude, sizeof(rr->loc.longitude), state);
+ siphash24_compress(&rr->loc.altitude, sizeof(rr->loc.altitude), state);
+ break;
+
+ case DNS_TYPE_SSHFP:
+ siphash24_compress(&rr->sshfp.algorithm, sizeof(rr->sshfp.algorithm), state);
+ siphash24_compress(&rr->sshfp.fptype, sizeof(rr->sshfp.fptype), state);
+ siphash24_compress(rr->sshfp.fingerprint, rr->sshfp.fingerprint_size, state);
+ break;
+
+ case DNS_TYPE_DNSKEY:
+ siphash24_compress(&rr->dnskey.flags, sizeof(rr->dnskey.flags), state);
+ siphash24_compress(&rr->dnskey.protocol, sizeof(rr->dnskey.protocol), state);
+ siphash24_compress(&rr->dnskey.algorithm, sizeof(rr->dnskey.algorithm), state);
+ siphash24_compress(rr->dnskey.key, rr->dnskey.key_size, state);
+ break;
+
+ case DNS_TYPE_RRSIG:
+ siphash24_compress(&rr->rrsig.type_covered, sizeof(rr->rrsig.type_covered), state);
+ siphash24_compress(&rr->rrsig.algorithm, sizeof(rr->rrsig.algorithm), state);
+ siphash24_compress(&rr->rrsig.labels, sizeof(rr->rrsig.labels), state);
+ siphash24_compress(&rr->rrsig.original_ttl, sizeof(rr->rrsig.original_ttl), state);
+ siphash24_compress(&rr->rrsig.expiration, sizeof(rr->rrsig.expiration), state);
+ siphash24_compress(&rr->rrsig.inception, sizeof(rr->rrsig.inception), state);
+ siphash24_compress(&rr->rrsig.key_tag, sizeof(rr->rrsig.key_tag), state);
+ dns_name_hash_func(rr->rrsig.signer, state);
+ siphash24_compress(rr->rrsig.signature, rr->rrsig.signature_size, state);
+ break;
+
+ case DNS_TYPE_NSEC:
+ dns_name_hash_func(rr->nsec.next_domain_name, state);
+ /* FIXME: we leave out the type bitmap here. Hash
+ * would be better if we'd take it into account
+ * too. */
+ break;
+
+ case DNS_TYPE_DS:
+ siphash24_compress(&rr->ds.key_tag, sizeof(rr->ds.key_tag), state);
+ siphash24_compress(&rr->ds.algorithm, sizeof(rr->ds.algorithm), state);
+ siphash24_compress(&rr->ds.digest_type, sizeof(rr->ds.digest_type), state);
+ siphash24_compress(rr->ds.digest, rr->ds.digest_size, state);
+ break;
+
+ case DNS_TYPE_NSEC3:
+ siphash24_compress(&rr->nsec3.algorithm, sizeof(rr->nsec3.algorithm), state);
+ siphash24_compress(&rr->nsec3.flags, sizeof(rr->nsec3.flags), state);
+ siphash24_compress(&rr->nsec3.iterations, sizeof(rr->nsec3.iterations), state);
+ siphash24_compress(rr->nsec3.salt, rr->nsec3.salt_size, state);
+ siphash24_compress(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, state);
+ /* FIXME: We leave the bitmaps out */
+ break;
+
+ default:
+ siphash24_compress(rr->generic.data, rr->generic.size, state);
+ break;
+ }
+}
+
+static int dns_resource_record_compare_func(const void *a, const void *b) {
+ const DnsResourceRecord *x = a, *y = b;
+ int ret;
+
+ ret = dns_resource_key_compare_func(x->key, y->key);
+ if (ret != 0)
+ return ret;
+
+ if (dns_resource_record_equal(x, y))
+ return 0;
+
+ /* This is a bit dirty, we don't implement proper odering, but
+ * the hashtable doesn't need ordering anyway, hence we don't
+ * care. */
+ return x < y ? -1 : 1;
+}
+
+const struct hash_ops dns_resource_record_hash_ops = {
+ .hash = dns_resource_record_hash_func,
+ .compare = dns_resource_record_compare_func,
+};
+
+DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i) {
+ DnsTxtItem *n;
+
+ if (!i)
+ return NULL;
+
+ n = i->items_next;
+
+ free(i);
+ return dns_txt_item_free_all(n);
+}
+
+bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b) {
+
+ if (a == b)
+ return true;
+
+ if (!a != !b)
+ return false;
+
+ if (!a)
+ return true;
+
+ if (a->length != b->length)
+ return false;
+
+ if (memcmp(a->data, b->data, a->length) != 0)
+ return false;
+
+ return dns_txt_item_equal(a->items_next, b->items_next);
+}
+
+static const char* const dnssec_algorithm_table[_DNSSEC_ALGORITHM_MAX_DEFINED] = {
+ /* Mnemonics as listed on https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml */
+ [DNSSEC_ALGORITHM_RSAMD5] = "RSAMD5",
+ [DNSSEC_ALGORITHM_DH] = "DH",
+ [DNSSEC_ALGORITHM_DSA] = "DSA",
+ [DNSSEC_ALGORITHM_ECC] = "ECC",
+ [DNSSEC_ALGORITHM_RSASHA1] = "RSASHA1",
+ [DNSSEC_ALGORITHM_DSA_NSEC3_SHA1] = "DSA-NSEC3-SHA1",
+ [DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1] = "RSASHA1-NSEC3-SHA1",
+ [DNSSEC_ALGORITHM_RSASHA256] = "RSASHA256",
+ [DNSSEC_ALGORITHM_RSASHA512] = "RSASHA512",
+ [DNSSEC_ALGORITHM_ECC_GOST] = "ECC-GOST",
+ [DNSSEC_ALGORITHM_ECDSAP256SHA256] = "ECDSAP256SHA256",
+ [DNSSEC_ALGORITHM_ECDSAP384SHA384] = "ECDSAP384SHA384",
+ [DNSSEC_ALGORITHM_INDIRECT] = "INDIRECT",
+ [DNSSEC_ALGORITHM_PRIVATEDNS] = "PRIVATEDNS",
+ [DNSSEC_ALGORITHM_PRIVATEOID] = "PRIVATEOID",
+};
+DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(dnssec_algorithm, int, 255);
+
+static const char* const dnssec_digest_table[_DNSSEC_DIGEST_MAX_DEFINED] = {
+ /* Names as listed on https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml */
+ [DNSSEC_DIGEST_SHA1] = "SHA-1",
+ [DNSSEC_DIGEST_SHA256] = "SHA-256",
+ [DNSSEC_DIGEST_GOST_R_34_11_94] = "GOST_R_34.11-94",
+ [DNSSEC_DIGEST_SHA384] = "SHA-384",
+};
+DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(dnssec_digest, int, 255);
diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h
index 9e2207c0aa..d9c31e81c5 100644
--- a/src/resolve/resolved-dns-rr.h
+++ b/src/resolve/resolved-dns-rr.h
@@ -24,37 +24,113 @@
#include <netinet/in.h>
#include "bitmap.h"
+#include "dns-type.h"
#include "hashmap.h"
#include "in-addr-util.h"
-#include "dns-type.h"
+#include "list.h"
typedef struct DnsResourceKey DnsResourceKey;
typedef struct DnsResourceRecord DnsResourceRecord;
+typedef struct DnsTxtItem DnsTxtItem;
+
+/* DNSKEY RR flags */
+#define DNSKEY_FLAG_SEP (UINT16_C(1) << 0)
+#define DNSKEY_FLAG_REVOKE (UINT16_C(1) << 7)
+#define DNSKEY_FLAG_ZONE_KEY (UINT16_C(1) << 8)
+
+/* mDNS RR flags */
+#define MDNS_RR_CACHE_FLUSH (UINT16_C(1) << 15)
+
+/* DNSSEC algorithm identifiers, see
+ * http://tools.ietf.org/html/rfc4034#appendix-A.1 and
+ * https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml */
+enum {
+ DNSSEC_ALGORITHM_RSAMD5 = 1,
+ DNSSEC_ALGORITHM_DH,
+ DNSSEC_ALGORITHM_DSA,
+ DNSSEC_ALGORITHM_ECC,
+ DNSSEC_ALGORITHM_RSASHA1,
+ DNSSEC_ALGORITHM_DSA_NSEC3_SHA1,
+ DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1,
+ DNSSEC_ALGORITHM_RSASHA256 = 8, /* RFC 5702 */
+ DNSSEC_ALGORITHM_RSASHA512 = 10, /* RFC 5702 */
+ DNSSEC_ALGORITHM_ECC_GOST = 12, /* RFC 5933 */
+ DNSSEC_ALGORITHM_ECDSAP256SHA256 = 13, /* RFC 6605 */
+ DNSSEC_ALGORITHM_ECDSAP384SHA384 = 14, /* RFC 6605 */
+ DNSSEC_ALGORITHM_INDIRECT = 252,
+ DNSSEC_ALGORITHM_PRIVATEDNS,
+ DNSSEC_ALGORITHM_PRIVATEOID,
+ _DNSSEC_ALGORITHM_MAX_DEFINED
+};
-/* DNS record classes, see RFC 1035 */
+/* DNSSEC digest identifiers, see
+ * https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml */
enum {
- DNS_CLASS_IN = 0x01,
- DNS_CLASS_ANY = 0xFF,
- _DNS_CLASS_MAX,
- _DNS_CLASS_INVALID = -1
+ DNSSEC_DIGEST_SHA1 = 1,
+ DNSSEC_DIGEST_SHA256 = 2, /* RFC 4509 */
+ DNSSEC_DIGEST_GOST_R_34_11_94 = 3, /* RFC 5933 */
+ DNSSEC_DIGEST_SHA384 = 4, /* RFC 6605 */
+ _DNSSEC_DIGEST_MAX_DEFINED
+};
+
+/* DNSSEC NSEC3 hash algorithms, see
+ * https://www.iana.org/assignments/dnssec-nsec3-parameters/dnssec-nsec3-parameters.xhtml */
+enum {
+ NSEC3_ALGORITHM_SHA1 = 1,
+ _NSEC3_ALGORITHM_MAX_DEFINED
};
struct DnsResourceKey {
- unsigned n_ref;
+ unsigned n_ref; /* (unsigned -1) for const keys, see below */
uint16_t class, type;
char *_name; /* don't access directy, use DNS_RESOURCE_KEY_NAME()! */
};
+/* Creates a temporary resource key. This is only useful to quickly
+ * look up something, without allocating a full DnsResourceKey object
+ * for it. Note that it is not OK to take references to this kind of
+ * resource key object. */
+#define DNS_RESOURCE_KEY_CONST(c, t, n) \
+ ((DnsResourceKey) { \
+ .n_ref = (unsigned) -1, \
+ .class = c, \
+ .type = t, \
+ ._name = (char*) n, \
+ })
+
+
+struct DnsTxtItem {
+ size_t length;
+ LIST_FIELDS(DnsTxtItem, items);
+ uint8_t data[];
+};
+
struct DnsResourceRecord {
unsigned n_ref;
DnsResourceKey *key;
+
+ char *to_string;
+
uint32_t ttl;
- bool unparseable;
+ usec_t expiry; /* RRSIG signature expiry */
+
+ /* How many labels to strip to determine "signer" of the RRSIG (aka, the zone). -1 if not signed. */
+ unsigned n_skip_labels_signer;
+ /* How many labels to strip to determine "synthesizing source" of this RR, i.e. the wildcard's immediate parent. -1 if not signed. */
+ unsigned n_skip_labels_source;
+
+ bool unparseable:1;
+
+ bool wire_format_canonical:1;
+ void *wire_format;
+ size_t wire_format_size;
+ size_t wire_format_rdata_offset;
+
union {
struct {
void *data;
size_t size;
- } generic;
+ } generic, opt;
struct {
uint16_t priority;
@@ -73,7 +149,7 @@ struct DnsResourceRecord {
} hinfo;
struct {
- char **strings;
+ DnsTxtItem *items;
} txt, spf;
struct {
@@ -99,6 +175,7 @@ struct DnsResourceRecord {
char *exchange;
} mx;
+ /* https://tools.ietf.org/html/rfc1876 */
struct {
uint8_t version;
uint8_t size;
@@ -109,14 +186,6 @@ struct DnsResourceRecord {
uint32_t altitude;
} loc;
- struct {
- uint16_t key_tag;
- uint8_t algorithm;
- uint8_t digest_type;
- void *digest;
- size_t digest_size;
- } ds;
-
/* https://tools.ietf.org/html/rfc4255#section-3.1 */
struct {
uint8_t algorithm;
@@ -127,8 +196,8 @@ struct DnsResourceRecord {
/* http://tools.ietf.org/html/rfc4034#section-2.1 */
struct {
- bool zone_key_flag:1;
- bool sep_flag:1;
+ uint16_t flags;
+ uint8_t protocol;
uint8_t algorithm;
void* key;
size_t key_size;
@@ -148,11 +217,21 @@ struct DnsResourceRecord {
size_t signature_size;
} rrsig;
+ /* https://tools.ietf.org/html/rfc4034#section-4.1 */
struct {
char *next_domain_name;
Bitmap *types;
} nsec;
+ /* https://tools.ietf.org/html/rfc4034#section-5.1 */
+ struct {
+ uint16_t key_tag;
+ uint8_t algorithm;
+ uint8_t digest_type;
+ void *digest;
+ size_t digest_size;
+ } ds;
+
struct {
uint8_t algorithm;
uint8_t flags;
@@ -167,7 +246,7 @@ struct DnsResourceRecord {
};
static inline const char* DNS_RESOURCE_KEY_NAME(const DnsResourceKey *key) {
- if (_unlikely_(!key))
+ if (!key)
return NULL;
if (key->_name)
@@ -176,18 +255,47 @@ static inline const char* DNS_RESOURCE_KEY_NAME(const DnsResourceKey *key) {
return (char*) key + sizeof(DnsResourceKey);
}
+static inline const void* DNS_RESOURCE_RECORD_RDATA(DnsResourceRecord *rr) {
+ if (!rr)
+ return NULL;
+
+ if (!rr->wire_format)
+ return NULL;
+
+ assert(rr->wire_format_rdata_offset <= rr->wire_format_size);
+ return (uint8_t*) rr->wire_format + rr->wire_format_rdata_offset;
+}
+
+static inline size_t DNS_RESOURCE_RECORD_RDATA_SIZE(DnsResourceRecord *rr) {
+ if (!rr)
+ return 0;
+ if (!rr->wire_format)
+ return 0;
+
+ assert(rr->wire_format_rdata_offset <= rr->wire_format_size);
+ return rr->wire_format_size - rr->wire_format_rdata_offset;
+}
+
DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name);
-DnsResourceKey* dns_resource_key_new_cname(const DnsResourceKey *key);
DnsResourceKey* dns_resource_key_new_redirect(const DnsResourceKey *key, const DnsResourceRecord *cname);
+int dns_resource_key_new_append_suffix(DnsResourceKey **ret, DnsResourceKey *key, char *name);
DnsResourceKey* dns_resource_key_new_consume(uint16_t class, uint16_t type, char *name);
DnsResourceKey* dns_resource_key_ref(DnsResourceKey *key);
DnsResourceKey* dns_resource_key_unref(DnsResourceKey *key);
+bool dns_resource_key_is_address(const DnsResourceKey *key);
int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b);
-int dns_resource_key_match_rr(const DnsResourceKey *key, const DnsResourceRecord *rr);
-int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRecord *rr);
+int dns_resource_key_match_rr(const DnsResourceKey *key, DnsResourceRecord *rr, const char *search_domain);
+int dns_resource_key_match_cname_or_dname(const DnsResourceKey *key, const DnsResourceKey *cname, const char *search_domain);
+int dns_resource_key_match_soa(const DnsResourceKey *key, const DnsResourceKey *soa);
int dns_resource_key_to_string(const DnsResourceKey *key, char **ret);
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceKey*, dns_resource_key_unref);
+static inline bool dns_key_is_shared(const DnsResourceKey *key) {
+ return IN_SET(key->type, DNS_TYPE_PTR);
+}
+
+bool dns_resource_key_reduce(DnsResourceKey **a, DnsResourceKey **b);
+
DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key);
DnsResourceRecord* dns_resource_record_new_full(uint16_t class, uint16_t type, const char *name);
DnsResourceRecord* dns_resource_record_ref(DnsResourceRecord *rr);
@@ -195,10 +303,24 @@ DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr);
int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *name);
int dns_resource_record_new_address(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *name);
int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b);
-int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret);
+const char* dns_resource_record_to_string(DnsResourceRecord *rr);
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceRecord*, dns_resource_record_unref);
-const char *dns_class_to_string(uint16_t type);
-int dns_class_from_string(const char *name, uint16_t *class);
+int dns_resource_record_to_wire_format(DnsResourceRecord *rr, bool canonical);
+
+int dns_resource_record_signer(DnsResourceRecord *rr, const char **ret);
+int dns_resource_record_source(DnsResourceRecord *rr, const char **ret);
+int dns_resource_record_is_signer(DnsResourceRecord *rr, const char *zone);
+int dns_resource_record_is_synthetic(DnsResourceRecord *rr);
+
+DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i);
+bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b);
extern const struct hash_ops dns_resource_key_hash_ops;
+extern const struct hash_ops dns_resource_record_hash_ops;
+
+int dnssec_algorithm_to_string_alloc(int i, char **ret);
+int dnssec_algorithm_from_string(const char *s) _pure_;
+
+int dnssec_digest_to_string_alloc(int i, char **ret);
+int dnssec_digest_from_string(const char *s) _pure_;
diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c
index 9e6f595a1b..8a52d66fad 100644
--- a/src/resolve/resolved-dns-scope.c
+++ b/src/resolve/resolved-dns-scope.c
@@ -21,15 +21,18 @@
#include <netinet/tcp.h>
-#include "missing.h"
-#include "strv.h"
-#include "socket-util.h"
#include "af-list.h"
-#include "random-util.h"
-#include "hostname-util.h"
+#include "alloc-util.h"
#include "dns-domain.h"
-#include "resolved-llmnr.h"
+#include "fd-util.h"
+#include "hostname-util.h"
+#include "missing.h"
+#include "random-util.h"
#include "resolved-dns-scope.h"
+#include "resolved-llmnr.h"
+#include "resolved-mdns.h"
+#include "socket-util.h"
+#include "strv.h"
#define MULTICAST_RATELIMIT_INTERVAL_USEC (1*USEC_PER_SEC)
#define MULTICAST_RATELIMIT_BURST 1000
@@ -54,9 +57,25 @@ int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int
s->family = family;
s->resend_timeout = MULTICAST_RESEND_TIMEOUT_MIN_USEC;
+ s->dnssec_mode = _DNSSEC_MODE_INVALID;
+
+ if (protocol == DNS_PROTOCOL_DNS) {
+ /* Copy DNSSEC mode from the link if it is set there,
+ * otherwise take the manager's DNSSEC mode. Note that
+ * we copy this only at scope creation time, and do
+ * not update it from the on, even if the setting
+ * changes. */
+
+ if (l)
+ s->dnssec_mode = link_get_dnssec_mode(l);
+ else
+ s->dnssec_mode = manager_get_dnssec_mode(m);
+ }
+
LIST_PREPEND(scopes, m->dns_scopes, s);
dns_scope_llmnr_membership(s, true);
+ dns_scope_mdns_membership(s, true);
log_debug("New scope on link %s, protocol %s, family %s", l ? l->name : "*", dns_protocol_to_string(protocol), family == AF_UNSPEC ? "*" : af_to_name(family));
@@ -67,29 +86,40 @@ int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int
return 0;
}
-DnsScope* dns_scope_free(DnsScope *s) {
- DnsTransaction *t;
- DnsResourceRecord *rr;
-
- if (!s)
- return NULL;
-
- log_debug("Removing scope on link %s, protocol %s, family %s", s->link ? s->link->name : "*", dns_protocol_to_string(s->protocol), s->family == AF_UNSPEC ? "*" : af_to_name(s->family));
+static void dns_scope_abort_transactions(DnsScope *s) {
+ assert(s);
- dns_scope_llmnr_membership(s, false);
+ while (s->transactions) {
+ DnsTransaction *t = s->transactions;
- while ((t = hashmap_steal_first(s->transactions))) {
/* Abort the transaction, but make sure it is not
* freed while we still look at it */
t->block_gc++;
- dns_transaction_complete(t, DNS_TRANSACTION_ABORTED);
+ if (DNS_TRANSACTION_IS_LIVE(t->state))
+ dns_transaction_complete(t, DNS_TRANSACTION_ABORTED);
t->block_gc--;
dns_transaction_free(t);
}
+}
+
+DnsScope* dns_scope_free(DnsScope *s) {
+ DnsResourceRecord *rr;
- hashmap_free(s->transactions);
+ if (!s)
+ return NULL;
+
+ log_debug("Removing scope on link %s, protocol %s, family %s", s->link ? s->link->name : "*", dns_protocol_to_string(s->protocol), s->family == AF_UNSPEC ? "*" : af_to_name(s->family));
+
+ dns_scope_llmnr_membership(s, false);
+ dns_scope_mdns_membership(s, false);
+ dns_scope_abort_transactions(s);
+
+ while (s->query_candidates)
+ dns_query_candidate_free(s->query_candidates);
+
+ hashmap_free(s->transactions_by_key);
while ((rr = ordered_hashmap_steal_first(s->conflict_queue)))
dns_resource_record_unref(rr);
@@ -101,7 +131,6 @@ DnsScope* dns_scope_free(DnsScope *s) {
dns_zone_flush(&s->zone);
LIST_REMOVE(scopes, s->manager->dns_scopes, s);
- strv_free(s->domains);
free(s);
return NULL;
@@ -134,11 +163,11 @@ void dns_scope_next_dns_server(DnsScope *s) {
void dns_scope_packet_received(DnsScope *s, usec_t rtt) {
assert(s);
- if (rtt > s->max_rtt) {
- s->max_rtt = rtt;
- s->resend_timeout = MIN(MAX(MULTICAST_RESEND_TIMEOUT_MIN_USEC, s->max_rtt * 2),
- MULTICAST_RESEND_TIMEOUT_MAX_USEC);
- }
+ if (rtt <= s->max_rtt)
+ return;
+
+ s->max_rtt = rtt;
+ s->resend_timeout = MIN(MAX(MULTICAST_RESEND_TIMEOUT_MIN_USEC, s->max_rtt * 2), MULTICAST_RESEND_TIMEOUT_MAX_USEC);
}
void dns_scope_packet_lost(DnsScope *s, usec_t usec) {
@@ -148,17 +177,15 @@ void dns_scope_packet_lost(DnsScope *s, usec_t usec) {
s->resend_timeout = MIN(s->resend_timeout * 2, MULTICAST_RESEND_TIMEOUT_MAX_USEC);
}
-int dns_scope_emit(DnsScope *s, int fd, DnsPacket *p) {
+static int dns_scope_emit_one(DnsScope *s, int fd, DnsPacket *p) {
union in_addr_union addr;
int ifindex = 0, r;
int family;
- uint16_t port;
uint32_t mtu;
assert(s);
assert(p);
assert(p->protocol == s->protocol);
- assert((s->protocol == DNS_PROTOCOL_DNS) != (fd < 0));
if (s->link) {
mtu = s->link->mtu;
@@ -167,7 +194,10 @@ int dns_scope_emit(DnsScope *s, int fd, DnsPacket *p) {
mtu = manager_find_mtu(s->manager);
switch (s->protocol) {
+
case DNS_PROTOCOL_DNS:
+ assert(fd >= 0);
+
if (DNS_PACKET_QDCOUNT(p) > 1)
return -EOPNOTSUPP;
@@ -184,6 +214,8 @@ int dns_scope_emit(DnsScope *s, int fd, DnsPacket *p) {
break;
case DNS_PROTOCOL_LLMNR:
+ assert(fd < 0);
+
if (DNS_PACKET_QDCOUNT(p) > 1)
return -EOPNOTSUPP;
@@ -191,7 +223,6 @@ int dns_scope_emit(DnsScope *s, int fd, DnsPacket *p) {
return -EBUSY;
family = s->family;
- port = LLMNR_PORT;
if (family == AF_INET) {
addr.in = LLMNR_MULTICAST_IPV4_ADDRESS;
@@ -204,7 +235,32 @@ int dns_scope_emit(DnsScope *s, int fd, DnsPacket *p) {
if (fd < 0)
return fd;
- r = manager_send(s->manager, fd, ifindex, family, &addr, port, p);
+ r = manager_send(s->manager, fd, ifindex, family, &addr, LLMNR_PORT, p);
+ if (r < 0)
+ return r;
+
+ break;
+
+ case DNS_PROTOCOL_MDNS:
+ assert(fd < 0);
+
+ if (!ratelimit_test(&s->ratelimit))
+ return -EBUSY;
+
+ family = s->family;
+
+ if (family == AF_INET) {
+ addr.in = MDNS_MULTICAST_IPV4_ADDRESS;
+ fd = manager_mdns_ipv4_fd(s->manager);
+ } else if (family == AF_INET6) {
+ addr.in6 = MDNS_MULTICAST_IPV6_ADDRESS;
+ fd = manager_mdns_ipv6_fd(s->manager);
+ } else
+ return -EAFNOSUPPORT;
+ if (fd < 0)
+ return fd;
+
+ r = manager_send(s->manager, fd, ifindex, family, &addr, MDNS_PORT, p);
if (r < 0)
return r;
@@ -217,8 +273,39 @@ int dns_scope_emit(DnsScope *s, int fd, DnsPacket *p) {
return 1;
}
-static int dns_scope_socket(DnsScope *s, int type, int family, const union in_addr_union *address, uint16_t port, DnsServer **server) {
- DnsServer *srv = NULL;
+int dns_scope_emit_udp(DnsScope *s, int fd, DnsPacket *p) {
+ int r;
+
+ assert(s);
+ assert(p);
+ assert(p->protocol == s->protocol);
+ assert((s->protocol == DNS_PROTOCOL_DNS) == (fd >= 0));
+
+ do {
+ /* If there are multiple linked packets, set the TC bit in all but the last of them */
+ if (p->more) {
+ assert(p->protocol == DNS_PROTOCOL_MDNS);
+ dns_packet_set_flags(p, true, true);
+ }
+
+ r = dns_scope_emit_one(s, fd, p);
+ if (r < 0)
+ return r;
+
+ p = p->more;
+ } while (p);
+
+ return 0;
+}
+
+static int dns_scope_socket(
+ DnsScope *s,
+ int type,
+ int family,
+ const union in_addr_union *address,
+ DnsServer *server,
+ uint16_t port) {
+
_cleanup_close_ int fd = -1;
union sockaddr_union sa = {};
socklen_t salen;
@@ -226,26 +313,27 @@ static int dns_scope_socket(DnsScope *s, int type, int family, const union in_ad
int ret, r;
assert(s);
- assert((family == AF_UNSPEC) == !address);
- if (family == AF_UNSPEC) {
- srv = dns_scope_get_dns_server(s);
- if (!srv)
- return -ESRCH;
+ if (server) {
+ assert(family == AF_UNSPEC);
+ assert(!address);
- sa.sa.sa_family = srv->family;
- if (srv->family == AF_INET) {
+ sa.sa.sa_family = server->family;
+ if (server->family == AF_INET) {
sa.in.sin_port = htobe16(port);
- sa.in.sin_addr = srv->address.in;
+ sa.in.sin_addr = server->address.in;
salen = sizeof(sa.in);
- } else if (srv->family == AF_INET6) {
+ } else if (server->family == AF_INET6) {
sa.in6.sin6_port = htobe16(port);
- sa.in6.sin6_addr = srv->address.in6;
+ sa.in6.sin6_addr = server->address.in6;
sa.in6.sin6_scope_id = s->link ? s->link->ifindex : 0;
salen = sizeof(sa.in6);
} else
return -EAFNOSUPPORT;
} else {
+ assert(family != AF_UNSPEC);
+ assert(address);
+
sa.sa.sa_family = family;
if (family == AF_INET) {
@@ -303,36 +391,34 @@ static int dns_scope_socket(DnsScope *s, int type, int family, const union in_ad
if (r < 0 && errno != EINPROGRESS)
return -errno;
- if (server)
- *server = srv;
-
ret = fd;
fd = -1;
return ret;
}
-int dns_scope_udp_dns_socket(DnsScope *s, DnsServer **server) {
- return dns_scope_socket(s, SOCK_DGRAM, AF_UNSPEC, NULL, 53, server);
+int dns_scope_socket_udp(DnsScope *s, DnsServer *server, uint16_t port) {
+ return dns_scope_socket(s, SOCK_DGRAM, AF_UNSPEC, NULL, server, port);
}
-int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port, DnsServer **server) {
- return dns_scope_socket(s, SOCK_STREAM, family, address, port, server);
+int dns_scope_socket_tcp(DnsScope *s, int family, const union in_addr_union *address, DnsServer *server, uint16_t port) {
+ return dns_scope_socket(s, SOCK_STREAM, family, address, server, port);
}
DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain) {
- char **i;
+ DnsSearchDomain *d;
assert(s);
assert(domain);
- if (ifindex != 0 && (!s->link || s->link->ifindex != ifindex))
- return DNS_SCOPE_NO;
+ /* Checks if the specified domain is something to look up on
+ * this scope. Note that this accepts non-qualified hostnames,
+ * i.e. those without any search path prefixed yet. */
- if ((SD_RESOLVED_FLAGS_MAKE(s->protocol, s->family) & flags) == 0)
+ if (ifindex != 0 && (!s->link || s->link->ifindex != ifindex))
return DNS_SCOPE_NO;
- if (dns_name_root(domain) != 0)
+ if ((SD_RESOLVED_FLAGS_MAKE(s->protocol, s->family, 0) & flags) == 0)
return DNS_SCOPE_NO;
/* Never resolve any loopback hostname or IP address via DNS,
@@ -343,15 +429,38 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co
dns_name_equal(domain, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0)
return DNS_SCOPE_NO;
- STRV_FOREACH(i, s->domains)
- if (dns_name_endswith(domain, *i) > 0)
+ /* Never respond to some of the domains listed in RFC6303 */
+ if (dns_name_endswith(domain, "0.in-addr.arpa") > 0 ||
+ dns_name_equal(domain, "255.255.255.255.in-addr.arpa") > 0 ||
+ dns_name_equal(domain, "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0)
+ return DNS_SCOPE_NO;
+
+ /* Never respond to some of the domains listed in RFC6761 */
+ if (dns_name_endswith(domain, "invalid") > 0)
+ return DNS_SCOPE_NO;
+
+ /* Always honour search domains for routing queries. Note that
+ * we return DNS_SCOPE_YES here, rather than just
+ * DNS_SCOPE_MAYBE, which means wildcard scopes won't be
+ * considered anymore. */
+ LIST_FOREACH(domains, d, dns_scope_get_search_domains(s))
+ if (dns_name_endswith(domain, d->name) > 0)
return DNS_SCOPE_YES;
switch (s->protocol) {
+
case DNS_PROTOCOL_DNS:
+
+ /* Exclude link-local IP ranges */
if (dns_name_endswith(domain, "254.169.in-addr.arpa") == 0 &&
- dns_name_endswith(domain, "0.8.e.f.ip6.arpa") == 0 &&
- dns_name_single_label(domain) == 0)
+ dns_name_endswith(domain, "8.e.f.ip6.arpa") == 0 &&
+ dns_name_endswith(domain, "9.e.f.ip6.arpa") == 0 &&
+ dns_name_endswith(domain, "a.e.f.ip6.arpa") == 0 &&
+ dns_name_endswith(domain, "b.e.f.ip6.arpa") == 0 &&
+ /* If networks use .local in their private setups, they are supposed to also add .local to their search
+ * domains, which we already checked above. Otherwise, we consider .local specific to mDNS and won't
+ * send such queries ordinary DNS servers. */
+ dns_name_endswith(domain, "local") == 0)
return DNS_SCOPE_MAYBE;
return DNS_SCOPE_NO;
@@ -369,7 +478,7 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co
case DNS_PROTOCOL_LLMNR:
if ((s->family == AF_INET && dns_name_endswith(domain, "in-addr.arpa") > 0) ||
(s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0) ||
- (dns_name_single_label(domain) > 0 && /* only resolve single label names via LLMNR */
+ (dns_name_is_single_label(domain) && /* only resolve single label names via LLMNR */
!is_gateway_hostname(domain) && /* don't resolve "gateway" with LLMNR, let nss-myhostname handle this */
manager_is_own_hostname(s->manager, domain) <= 0)) /* never resolve the local hostname via LLMNR */
return DNS_SCOPE_MAYBE;
@@ -385,8 +494,27 @@ int dns_scope_good_key(DnsScope *s, DnsResourceKey *key) {
assert(s);
assert(key);
- if (s->protocol == DNS_PROTOCOL_DNS)
- return true;
+ /* Check if it makes sense to resolve the specified key on
+ * this scope. Note that this call assumes as fully qualified
+ * name, i.e. the search suffixes already appended. */
+
+ if (s->protocol == DNS_PROTOCOL_DNS) {
+
+ /* On classic DNS, looking up non-address RRs is always
+ * fine. (Specifically, we want to permit looking up
+ * DNSKEY and DS records on the root and top-level
+ * domains.) */
+ if (!dns_resource_key_is_address(key))
+ return true;
+
+ /* However, we refuse to look up A and AAAA RRs on the
+ * root and single-label domains, under the assumption
+ * that those should be resolved via LLMNR or search
+ * path only, and should not be leaked onto the
+ * internet. */
+ return !(dns_name_is_single_label(DNS_RESOURCE_KEY_NAME(key)) ||
+ dns_name_is_root(DNS_RESOURCE_KEY_NAME(key)));
+ }
/* On mDNS and LLMNR, send A and AAAA queries only on the
* respective scopes */
@@ -400,19 +528,15 @@ int dns_scope_good_key(DnsScope *s, DnsResourceKey *key) {
return true;
}
-int dns_scope_llmnr_membership(DnsScope *s, bool b) {
+static int dns_scope_multicast_membership(DnsScope *s, bool b, struct in_addr in, struct in6_addr in6) {
int fd;
assert(s);
-
- if (s->protocol != DNS_PROTOCOL_LLMNR)
- return 0;
-
assert(s->link);
if (s->family == AF_INET) {
struct ip_mreqn mreqn = {
- .imr_multiaddr = LLMNR_MULTICAST_IPV4_ADDRESS,
+ .imr_multiaddr = in,
.imr_ifindex = s->link->ifindex,
};
@@ -431,7 +555,7 @@ int dns_scope_llmnr_membership(DnsScope *s, bool b) {
} else if (s->family == AF_INET6) {
struct ipv6_mreq mreq = {
- .ipv6mr_multiaddr = LLMNR_MULTICAST_IPV6_ADDRESS,
+ .ipv6mr_multiaddr = in6,
.ipv6mr_interface = s->link->ifindex,
};
@@ -450,6 +574,22 @@ int dns_scope_llmnr_membership(DnsScope *s, bool b) {
return 0;
}
+int dns_scope_llmnr_membership(DnsScope *s, bool b) {
+
+ if (s->protocol != DNS_PROTOCOL_LLMNR)
+ return 0;
+
+ return dns_scope_multicast_membership(s, b, LLMNR_MULTICAST_IPV4_ADDRESS, LLMNR_MULTICAST_IPV6_ADDRESS);
+}
+
+int dns_scope_mdns_membership(DnsScope *s, bool b) {
+
+ if (s->protocol != DNS_PROTOCOL_MDNS)
+ return 0;
+
+ return dns_scope_multicast_membership(s, b, MDNS_MULTICAST_IPV4_ADDRESS, MDNS_MULTICAST_IPV6_ADDRESS);
+}
+
static int dns_scope_make_reply_packet(
DnsScope *s,
uint16_t id,
@@ -500,7 +640,7 @@ static int dns_scope_make_reply_packet(
if (answer) {
for (i = 0; i < answer->n_rrs; i++) {
- r = dns_packet_append_rr(p, answer->items[i].rr, NULL);
+ r = dns_packet_append_rr(p, answer->items[i].rr, NULL, NULL);
if (r < 0)
return r;
}
@@ -510,7 +650,7 @@ static int dns_scope_make_reply_packet(
if (soa) {
for (i = 0; i < soa->n_rrs; i++) {
- r = dns_packet_append_rr(p, soa->items[i].rr, NULL);
+ r = dns_packet_append_rr(p, soa->items[i].rr, NULL, NULL);
if (r < 0)
return r;
}
@@ -541,6 +681,7 @@ static void dns_scope_verify_conflicts(DnsScope *s, DnsPacket *p) {
void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
_cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
+ DnsResourceKey *key = NULL;
bool tentative = false;
int r, fd;
@@ -574,7 +715,10 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
return;
}
- r = dns_zone_lookup(&s->zone, p->question, &answer, &soa, &tentative);
+ assert(p->question->n_keys == 1);
+ key = p->question->keys[0];
+
+ r = dns_zone_lookup(&s->zone, key, &answer, &soa, &tentative);
if (r < 0) {
log_debug_errno(r, "Failed to lookup key: %m");
return;
@@ -632,15 +776,15 @@ DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsResourceKey *key,
/* Try to find an ongoing transaction that is a equal to the
* specified question */
- t = hashmap_get(scope->transactions, key);
+ t = hashmap_get(scope->transactions_by_key, key);
if (!t)
return NULL;
/* Refuse reusing transactions that completed based on cached
* data instead of a real packet, if that's requested. */
if (!cache_ok &&
- IN_SET(t->state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_FAILURE) &&
- !t->received)
+ IN_SET(t->state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_RCODE_FAILURE) &&
+ t->answer_source != DNS_TRANSACTION_NETWORK)
return NULL;
return t;
@@ -672,7 +816,11 @@ static int dns_scope_make_conflict_packet(
0 /* (ad) */,
0 /* (cd) */,
0));
- random_bytes(&DNS_PACKET_HEADER(p)->id, sizeof(uint16_t));
+
+ /* For mDNS, the transaction ID should always be 0 */
+ if (s->protocol != DNS_PROTOCOL_MDNS)
+ random_bytes(&DNS_PACKET_HEADER(p)->id, sizeof(uint16_t));
+
DNS_PACKET_HEADER(p)->qdcount = htobe16(1);
DNS_PACKET_HEADER(p)->arcount = htobe16(1);
@@ -680,7 +828,7 @@ static int dns_scope_make_conflict_packet(
if (r < 0)
return r;
- r = dns_packet_append_rr(p, rr, NULL);
+ r = dns_packet_append_rr(p, rr, NULL, NULL);
if (r < 0)
return r;
@@ -713,7 +861,7 @@ static int on_conflict_dispatch(sd_event_source *es, usec_t usec, void *userdata
return 0;
}
- r = dns_scope_emit(scope, -1, p);
+ r = dns_scope_emit_udp(scope, -1, p);
if (r < 0)
log_debug_errno(r, "Failed to send conflict packet: %m");
}
@@ -762,6 +910,8 @@ int dns_scope_notify_conflict(DnsScope *scope, DnsResourceRecord *rr) {
if (r < 0)
return log_debug_errno(r, "Failed to add conflict dispatch event: %m");
+ (void) sd_event_source_set_description(scope->conflict_event_source, "scope-conflict");
+
return 0;
}
@@ -844,3 +994,24 @@ void dns_scope_dump(DnsScope *s, FILE *f) {
dns_cache_dump(&s->cache, f);
}
}
+
+DnsSearchDomain *dns_scope_get_search_domains(DnsScope *s) {
+ assert(s);
+
+ if (s->protocol != DNS_PROTOCOL_DNS)
+ return NULL;
+
+ if (s->link)
+ return s->link->search_domains;
+
+ return s->manager->search_domains;
+}
+
+bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name) {
+ assert(s);
+
+ if (s->protocol != DNS_PROTOCOL_DNS)
+ return false;
+
+ return dns_name_is_single_label(name);
+}
diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h
index b75f212897..a0676bd30e 100644
--- a/src/resolve/resolved-dns-scope.h
+++ b/src/resolve/resolved-dns-scope.h
@@ -25,9 +25,10 @@
typedef struct DnsScope DnsScope;
-#include "resolved-dns-server.h"
-#include "resolved-dns-packet.h"
#include "resolved-dns-cache.h"
+#include "resolved-dns-dnssec.h"
+#include "resolved-dns-packet.h"
+#include "resolved-dns-server.h"
#include "resolved-dns-zone.h"
#include "resolved-link.h"
@@ -44,11 +45,10 @@ struct DnsScope {
DnsProtocol protocol;
int family;
+ DnssecMode dnssec_mode;
Link *link;
- char **domains;
-
DnsCache cache;
DnsZone zone;
@@ -60,7 +60,18 @@ struct DnsScope {
usec_t resend_timeout;
usec_t max_rtt;
- Hashmap *transactions;
+ LIST_HEAD(DnsQueryCandidate, query_candidates);
+
+ /* Note that we keep track of ongoing transactions in two
+ * ways: once in a hashmap, indexed by the rr key, and once in
+ * a linked list. We use the hashmap to quickly find
+ * transactions we can reuse for a key. But note that there
+ * might be multiple transactions for the same key (because
+ * the zone probing can't reuse a transaction answered from
+ * the zone or the cache), and the hashmap only tracks the
+ * most recent entry. */
+ Hashmap *transactions_by_key;
+ LIST_HEAD(DnsTransaction, transactions);
LIST_FIELDS(DnsScope, scopes);
};
@@ -71,9 +82,9 @@ DnsScope* dns_scope_free(DnsScope *s);
void dns_scope_packet_received(DnsScope *s, usec_t rtt);
void dns_scope_packet_lost(DnsScope *s, usec_t usec);
-int dns_scope_emit(DnsScope *s, int fd, DnsPacket *p);
-int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port, DnsServer **server);
-int dns_scope_udp_dns_socket(DnsScope *s, DnsServer **server);
+int dns_scope_emit_udp(DnsScope *s, int fd, DnsPacket *p);
+int dns_scope_socket_tcp(DnsScope *s, int family, const union in_addr_union *address, DnsServer *server, uint16_t port);
+int dns_scope_socket_udp(DnsScope *s, DnsServer *server, uint16_t port);
DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain);
int dns_scope_good_key(DnsScope *s, DnsResourceKey *key);
@@ -82,6 +93,7 @@ DnsServer *dns_scope_get_dns_server(DnsScope *s);
void dns_scope_next_dns_server(DnsScope *s);
int dns_scope_llmnr_membership(DnsScope *s, bool b);
+int dns_scope_mdns_membership(DnsScope *s, bool b);
void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p);
@@ -91,3 +103,7 @@ int dns_scope_notify_conflict(DnsScope *scope, DnsResourceRecord *rr);
void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p);
void dns_scope_dump(DnsScope *s, FILE *f);
+
+DnsSearchDomain *dns_scope_get_search_domains(DnsScope *s);
+
+bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name);
diff --git a/src/resolve/resolved-dns-search-domain.c b/src/resolve/resolved-dns-search-domain.c
new file mode 100644
index 0000000000..f9d966abb1
--- /dev/null
+++ b/src/resolve/resolved-dns-search-domain.c
@@ -0,0 +1,232 @@
+/*-*- 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 "alloc-util.h"
+#include "dns-domain.h"
+#include "resolved-dns-search-domain.h"
+
+int dns_search_domain_new(
+ Manager *m,
+ DnsSearchDomain **ret,
+ DnsSearchDomainType type,
+ Link *l,
+ const char *name) {
+
+ _cleanup_free_ char *normalized = NULL;
+ DnsSearchDomain *d;
+ int r;
+
+ assert(m);
+ assert((type == DNS_SEARCH_DOMAIN_LINK) == !!l);
+ assert(name);
+
+ r = dns_name_normalize(name, &normalized);
+ if (r < 0)
+ return r;
+
+ if (dns_name_is_root(normalized))
+ return -EINVAL;
+
+ if (l) {
+ if (l->n_search_domains >= LINK_SEARCH_DOMAINS_MAX)
+ return -E2BIG;
+ } else {
+ if (m->n_search_domains >= MANAGER_SEARCH_DOMAINS_MAX)
+ return -E2BIG;
+ }
+
+ d = new0(DnsSearchDomain, 1);
+ if (!d)
+ return -ENOMEM;
+
+ d->n_ref = 1;
+ d->manager = m;
+ d->type = type;
+ d->name = normalized;
+ normalized = NULL;
+
+ switch (type) {
+
+ case DNS_SEARCH_DOMAIN_LINK:
+ d->link = l;
+ LIST_APPEND(domains, l->search_domains, d);
+ l->n_search_domains++;
+ break;
+
+ case DNS_SERVER_SYSTEM:
+ LIST_APPEND(domains, m->search_domains, d);
+ m->n_search_domains++;
+ break;
+
+ default:
+ assert_not_reached("Unknown search domain type");
+ }
+
+ d->linked = true;
+
+ if (ret)
+ *ret = d;
+
+ return 0;
+}
+
+DnsSearchDomain* dns_search_domain_ref(DnsSearchDomain *d) {
+ if (!d)
+ return NULL;
+
+ assert(d->n_ref > 0);
+ d->n_ref++;
+
+ return d;
+}
+
+DnsSearchDomain* dns_search_domain_unref(DnsSearchDomain *d) {
+ if (!d)
+ return NULL;
+
+ assert(d->n_ref > 0);
+ d->n_ref--;
+
+ if (d->n_ref > 0)
+ return NULL;
+
+ free(d->name);
+ free(d);
+
+ return NULL;
+}
+
+void dns_search_domain_unlink(DnsSearchDomain *d) {
+ assert(d);
+ assert(d->manager);
+
+ if (!d->linked)
+ return;
+
+ switch (d->type) {
+
+ case DNS_SEARCH_DOMAIN_LINK:
+ assert(d->link);
+ assert(d->link->n_search_domains > 0);
+ LIST_REMOVE(domains, d->link->search_domains, d);
+ d->link->n_search_domains--;
+ break;
+
+ case DNS_SEARCH_DOMAIN_SYSTEM:
+ assert(d->manager->n_search_domains > 0);
+ LIST_REMOVE(domains, d->manager->search_domains, d);
+ d->manager->n_search_domains--;
+ break;
+ }
+
+ d->linked = false;
+
+ dns_search_domain_unref(d);
+}
+
+void dns_search_domain_move_back_and_unmark(DnsSearchDomain *d) {
+ DnsSearchDomain *tail;
+
+ assert(d);
+
+ if (!d->marked)
+ return;
+
+ d->marked = false;
+
+ if (!d->linked || !d->domains_next)
+ return;
+
+ switch (d->type) {
+
+ case DNS_SEARCH_DOMAIN_LINK:
+ assert(d->link);
+ LIST_FIND_TAIL(domains, d, tail);
+ LIST_REMOVE(domains, d->link->search_domains, d);
+ LIST_INSERT_AFTER(domains, d->link->search_domains, tail, d);
+ break;
+
+ case DNS_SEARCH_DOMAIN_SYSTEM:
+ LIST_FIND_TAIL(domains, d, tail);
+ LIST_REMOVE(domains, d->manager->search_domains, d);
+ LIST_INSERT_AFTER(domains, d->manager->search_domains, tail, d);
+ break;
+
+ default:
+ assert_not_reached("Unknown search domain type");
+ }
+}
+
+void dns_search_domain_unlink_all(DnsSearchDomain *first) {
+ DnsSearchDomain *next;
+
+ if (!first)
+ return;
+
+ next = first->domains_next;
+ dns_search_domain_unlink(first);
+
+ dns_search_domain_unlink_all(next);
+}
+
+void dns_search_domain_unlink_marked(DnsSearchDomain *first) {
+ DnsSearchDomain *next;
+
+ if (!first)
+ return;
+
+ next = first->domains_next;
+
+ if (first->marked)
+ dns_search_domain_unlink(first);
+
+ dns_search_domain_unlink_marked(next);
+}
+
+void dns_search_domain_mark_all(DnsSearchDomain *first) {
+ if (!first)
+ return;
+
+ first->marked = true;
+ dns_search_domain_mark_all(first->domains_next);
+}
+
+int dns_search_domain_find(DnsSearchDomain *first, const char *name, DnsSearchDomain **ret) {
+ DnsSearchDomain *d;
+ int r;
+
+ assert(name);
+ assert(ret);
+
+ LIST_FOREACH(domains, d, first) {
+
+ r = dns_name_equal(name, d->name);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ *ret = d;
+ return 1;
+ }
+ }
+
+ *ret = NULL;
+ return 0;
+}
diff --git a/src/resolve/resolved-dns-search-domain.h b/src/resolve/resolved-dns-search-domain.h
new file mode 100644
index 0000000000..2e0af31dda
--- /dev/null
+++ b/src/resolve/resolved-dns-search-domain.h
@@ -0,0 +1,75 @@
+/*-*- 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 "macro.h"
+
+typedef struct DnsSearchDomain DnsSearchDomain;
+
+typedef enum DnsSearchDomainType {
+ DNS_SEARCH_DOMAIN_SYSTEM,
+ DNS_SEARCH_DOMAIN_LINK,
+} DnsSearchDomainType;
+
+#include "resolved-link.h"
+#include "resolved-manager.h"
+
+struct DnsSearchDomain {
+ Manager *manager;
+
+ unsigned n_ref;
+
+ DnsSearchDomainType type;
+ Link *link;
+
+ char *name;
+
+ bool marked:1;
+
+ bool linked:1;
+ LIST_FIELDS(DnsSearchDomain, domains);
+};
+
+int dns_search_domain_new(
+ Manager *m,
+ DnsSearchDomain **ret,
+ DnsSearchDomainType type,
+ Link *link,
+ const char *name);
+
+DnsSearchDomain* dns_search_domain_ref(DnsSearchDomain *d);
+DnsSearchDomain* dns_search_domain_unref(DnsSearchDomain *d);
+
+void dns_search_domain_unlink(DnsSearchDomain *d);
+void dns_search_domain_move_back_and_unmark(DnsSearchDomain *d);
+
+void dns_search_domain_unlink_all(DnsSearchDomain *first);
+void dns_search_domain_unlink_marked(DnsSearchDomain *first);
+void dns_search_domain_mark_all(DnsSearchDomain *first);
+
+int dns_search_domain_find(DnsSearchDomain *first, const char *name, DnsSearchDomain **ret);
+
+static inline const char* DNS_SEARCH_DOMAIN_NAME(DnsSearchDomain *d) {
+ return d ? d->name : NULL;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(DnsSearchDomain*, dns_search_domain_unref);
diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c
index 2ff5b192df..5a86661807 100644
--- a/src/resolve/resolved-dns-server.c
+++ b/src/resolve/resolved-dns-server.c
@@ -19,14 +19,24 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "siphash24.h"
-
+#include "alloc-util.h"
#include "resolved-dns-server.h"
+#include "resolved-resolv-conf.h"
+#include "siphash24.h"
+#include "string-table.h"
+#include "string-util.h"
/* After how much time to repeat classic DNS requests */
#define DNS_TIMEOUT_MIN_USEC (500 * USEC_PER_MSEC)
#define DNS_TIMEOUT_MAX_USEC (5 * USEC_PER_SEC)
+/* The amount of time to wait before retrying with a full feature set */
+#define DNS_SERVER_FEATURE_GRACE_PERIOD_MAX_USEC (6 * USEC_PER_HOUR)
+#define DNS_SERVER_FEATURE_GRACE_PERIOD_MIN_USEC (5 * USEC_PER_MINUTE)
+
+/* The number of times we will attempt a certain feature set before degrading */
+#define DNS_SERVER_FEATURE_RETRY_ATTEMPTS 3
+
int dns_server_new(
Manager *m,
DnsServer **ret,
@@ -35,36 +45,61 @@ int dns_server_new(
int family,
const union in_addr_union *in_addr) {
- DnsServer *s, *tail;
+ DnsServer *s;
assert(m);
assert((type == DNS_SERVER_LINK) == !!l);
assert(in_addr);
+ if (!IN_SET(family, AF_INET, AF_INET6))
+ return -EAFNOSUPPORT;
+
+ if (l) {
+ if (l->n_dns_servers >= LINK_DNS_SERVERS_MAX)
+ return -E2BIG;
+ } else {
+ if (m->n_dns_servers >= MANAGER_DNS_SERVERS_MAX)
+ return -E2BIG;
+ }
+
s = new0(DnsServer, 1);
if (!s)
return -ENOMEM;
s->n_ref = 1;
+ s->manager = m;
+ s->verified_feature_level = _DNS_SERVER_FEATURE_LEVEL_INVALID;
+ s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_BEST;
+ s->features_grace_period_usec = DNS_SERVER_FEATURE_GRACE_PERIOD_MIN_USEC;
+ s->received_udp_packet_max = DNS_PACKET_UNICAST_SIZE_MAX;
s->type = type;
s->family = family;
s->address = *in_addr;
s->resend_timeout = DNS_TIMEOUT_MIN_USEC;
- if (type == DNS_SERVER_LINK) {
- LIST_FIND_TAIL(servers, l->dns_servers, tail);
- LIST_INSERT_AFTER(servers, l->dns_servers, tail, s);
+ switch (type) {
+
+ case DNS_SERVER_LINK:
s->link = l;
- } else if (type == DNS_SERVER_SYSTEM) {
- LIST_FIND_TAIL(servers, m->dns_servers, tail);
- LIST_INSERT_AFTER(servers, m->dns_servers, tail, s);
- } else if (type == DNS_SERVER_FALLBACK) {
- LIST_FIND_TAIL(servers, m->fallback_dns_servers, tail);
- LIST_INSERT_AFTER(servers, m->fallback_dns_servers, tail, s);
- } else
+ LIST_APPEND(servers, l->dns_servers, s);
+ l->n_dns_servers++;
+ break;
+
+ case DNS_SERVER_SYSTEM:
+ LIST_APPEND(servers, m->dns_servers, s);
+ m->n_dns_servers++;
+ break;
+
+ case DNS_SERVER_FALLBACK:
+ LIST_APPEND(servers, m->fallback_dns_servers, s);
+ m->n_dns_servers++;
+ break;
+
+ default:
assert_not_reached("Unknown server type");
+ }
- s->manager = m;
+ s->linked = true;
/* A new DNS server that isn't fallback is added and the one
* we used so far was a fallback one? Then let's try to pick
@@ -85,66 +120,440 @@ DnsServer* dns_server_ref(DnsServer *s) {
return NULL;
assert(s->n_ref > 0);
-
s->n_ref ++;
return s;
}
-static DnsServer* dns_server_free(DnsServer *s) {
+DnsServer* dns_server_unref(DnsServer *s) {
if (!s)
return NULL;
+ assert(s->n_ref > 0);
+ s->n_ref --;
+
+ if (s->n_ref > 0)
+ return NULL;
+
+ free(s->server_string);
+ free(s);
+ return NULL;
+}
+
+void dns_server_unlink(DnsServer *s) {
+ assert(s);
+ assert(s->manager);
+
+ /* This removes the specified server from the linked list of
+ * servers, but any server might still stay around if it has
+ * refs, for example from an ongoing transaction. */
+
+ if (!s->linked)
+ return;
+
+ switch (s->type) {
+
+ case DNS_SERVER_LINK:
+ assert(s->link);
+ assert(s->link->n_dns_servers > 0);
+ LIST_REMOVE(servers, s->link->dns_servers, s);
+ break;
+
+ case DNS_SERVER_SYSTEM:
+ assert(s->manager->n_dns_servers > 0);
+ LIST_REMOVE(servers, s->manager->dns_servers, s);
+ s->manager->n_dns_servers--;
+ break;
+
+ case DNS_SERVER_FALLBACK:
+ assert(s->manager->n_dns_servers > 0);
+ LIST_REMOVE(servers, s->manager->fallback_dns_servers, s);
+ s->manager->n_dns_servers--;
+ break;
+ }
+
+ s->linked = false;
+
if (s->link && s->link->current_dns_server == s)
link_set_dns_server(s->link, NULL);
- if (s->manager && s->manager->current_dns_server == s)
+ if (s->manager->current_dns_server == s)
manager_set_dns_server(s->manager, NULL);
- free(s);
+ dns_server_unref(s);
+}
- return NULL;
+void dns_server_move_back_and_unmark(DnsServer *s) {
+ DnsServer *tail;
+
+ assert(s);
+
+ if (!s->marked)
+ return;
+
+ s->marked = false;
+
+ if (!s->linked || !s->servers_next)
+ return;
+
+ /* Move us to the end of the list, so that the order is
+ * strictly kept, if we are not at the end anyway. */
+
+ switch (s->type) {
+
+ case DNS_SERVER_LINK:
+ assert(s->link);
+ LIST_FIND_TAIL(servers, s, tail);
+ LIST_REMOVE(servers, s->link->dns_servers, s);
+ LIST_INSERT_AFTER(servers, s->link->dns_servers, tail, s);
+ break;
+
+ case DNS_SERVER_SYSTEM:
+ LIST_FIND_TAIL(servers, s, tail);
+ LIST_REMOVE(servers, s->manager->dns_servers, s);
+ LIST_INSERT_AFTER(servers, s->manager->dns_servers, tail, s);
+ break;
+
+ case DNS_SERVER_FALLBACK:
+ LIST_FIND_TAIL(servers, s, tail);
+ LIST_REMOVE(servers, s->manager->fallback_dns_servers, s);
+ LIST_INSERT_AFTER(servers, s->manager->fallback_dns_servers, tail, s);
+ break;
+
+ default:
+ assert_not_reached("Unknown server type");
+ }
}
-DnsServer* dns_server_unref(DnsServer *s) {
- if (!s)
- return NULL;
+static void dns_server_verified(DnsServer *s, DnsServerFeatureLevel level) {
+ assert(s);
- assert(s->n_ref > 0);
+ if (s->verified_feature_level > level)
+ return;
- if (s->n_ref == 1)
- dns_server_free(s);
- else
- s->n_ref --;
+ if (s->verified_feature_level != level) {
+ log_debug("Verified we get a response at feature level %s from DNS server %s.",
+ dns_server_feature_level_to_string(level),
+ dns_server_string(s));
+ s->verified_feature_level = level;
+ }
- return NULL;
+ assert_se(sd_event_now(s->manager->event, clock_boottime_or_monotonic(), &s->verified_usec) >= 0);
}
-void dns_server_packet_received(DnsServer *s, usec_t rtt) {
+void dns_server_packet_received(DnsServer *s, int protocol, DnsServerFeatureLevel level, usec_t rtt, size_t size) {
assert(s);
- if (rtt > s->max_rtt) {
+ if (protocol == IPPROTO_UDP) {
+ if (s->possible_feature_level == level)
+ s->n_failed_udp = 0;
+
+ /* If the RRSIG data is missing, then we can only validate EDNS0 at max */
+ if (s->packet_rrsig_missing && level >= DNS_SERVER_FEATURE_LEVEL_DO)
+ level = DNS_SERVER_FEATURE_LEVEL_DO - 1;
+
+ /* If the OPT RR got lost, then we can only validate UDP at max */
+ if (s->packet_bad_opt && level >= DNS_SERVER_FEATURE_LEVEL_EDNS0)
+ level = DNS_SERVER_FEATURE_LEVEL_EDNS0 - 1;
+
+ /* Even if we successfully receive a reply to a request announcing support for large packets,
+ that does not mean we can necessarily receive large packets. */
+ if (level == DNS_SERVER_FEATURE_LEVEL_LARGE)
+ level = DNS_SERVER_FEATURE_LEVEL_LARGE - 1;
+
+ } else if (protocol == IPPROTO_TCP) {
+
+ if (s->possible_feature_level == level)
+ s->n_failed_tcp = 0;
+
+ /* Successful TCP connections are only useful to verify the TCP feature level. */
+ level = DNS_SERVER_FEATURE_LEVEL_TCP;
+ }
+
+ dns_server_verified(s, level);
+
+ /* Remember the size of the largest UDP packet we received from a server,
+ we know that we can always announce support for packets with at least
+ this size. */
+ if (protocol == IPPROTO_UDP && s->received_udp_packet_max < size)
+ s->received_udp_packet_max = size;
+
+ if (s->max_rtt < rtt) {
s->max_rtt = rtt;
- s->resend_timeout = MIN(MAX(DNS_TIMEOUT_MIN_USEC, s->max_rtt * 2),
- DNS_TIMEOUT_MAX_USEC);
+ s->resend_timeout = CLAMP(s->max_rtt * 2, DNS_TIMEOUT_MIN_USEC, DNS_TIMEOUT_MAX_USEC);
+ }
+}
+
+void dns_server_packet_lost(DnsServer *s, int protocol, DnsServerFeatureLevel level, usec_t usec) {
+ assert(s);
+ assert(s->manager);
+
+ if (s->possible_feature_level == level) {
+ if (protocol == IPPROTO_UDP)
+ s->n_failed_udp ++;
+ else if (protocol == IPPROTO_TCP)
+ s->n_failed_tcp ++;
}
+
+ if (s->resend_timeout > usec)
+ return;
+
+ s->resend_timeout = MIN(s->resend_timeout * 2, DNS_TIMEOUT_MAX_USEC);
+}
+
+void dns_server_packet_failed(DnsServer *s, DnsServerFeatureLevel level) {
+ assert(s);
+
+ /* Invoked whenever we get a FORMERR, SERVFAIL or NOTIMP rcode from a server. */
+
+ if (s->possible_feature_level != level)
+ return;
+
+ s->packet_failed = true;
+}
+
+void dns_server_packet_truncated(DnsServer *s, DnsServerFeatureLevel level) {
+ assert(s);
+
+ /* Invoked whenever we get a packet with TC bit set. */
+
+ if (s->possible_feature_level != level)
+ return;
+
+ s->packet_truncated = true;
+}
+
+void dns_server_packet_rrsig_missing(DnsServer *s, DnsServerFeatureLevel level) {
+ assert(s);
+
+ if (level < DNS_SERVER_FEATURE_LEVEL_DO)
+ return;
+
+ /* If the RRSIG RRs are missing, we have to downgrade what we previously verified */
+ if (s->verified_feature_level >= DNS_SERVER_FEATURE_LEVEL_DO)
+ s->verified_feature_level = DNS_SERVER_FEATURE_LEVEL_DO-1;
+
+ s->packet_rrsig_missing = true;
}
-void dns_server_packet_lost(DnsServer *s, usec_t usec) {
+void dns_server_packet_bad_opt(DnsServer *s, DnsServerFeatureLevel level) {
assert(s);
- if (s->resend_timeout <= usec)
- s->resend_timeout = MIN(s->resend_timeout * 2, DNS_TIMEOUT_MAX_USEC);
+ if (level < DNS_SERVER_FEATURE_LEVEL_EDNS0)
+ return;
+
+ /* If the OPT RR got lost, we have to downgrade what we previously verified */
+ if (s->verified_feature_level >= DNS_SERVER_FEATURE_LEVEL_EDNS0)
+ s->verified_feature_level = DNS_SERVER_FEATURE_LEVEL_EDNS0-1;
+
+ s->packet_bad_opt = true;
}
-static unsigned long dns_server_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
+static bool dns_server_grace_period_expired(DnsServer *s) {
+ usec_t ts;
+
+ assert(s);
+ assert(s->manager);
+
+ if (s->verified_usec == 0)
+ return false;
+
+ assert_se(sd_event_now(s->manager->event, clock_boottime_or_monotonic(), &ts) >= 0);
+
+ if (s->verified_usec + s->features_grace_period_usec > ts)
+ return false;
+
+ s->features_grace_period_usec = MIN(s->features_grace_period_usec * 2, DNS_SERVER_FEATURE_GRACE_PERIOD_MAX_USEC);
+
+ return true;
+}
+
+static void dns_server_reset_counters(DnsServer *s) {
+ assert(s);
+
+ s->n_failed_udp = 0;
+ s->n_failed_tcp = 0;
+ s->packet_failed = false;
+ s->packet_truncated = false;
+ s->verified_usec = 0;
+
+ /* Note that we do not reset s->packet_bad_opt and s->packet_rrsig_missing here. We reset them only when the
+ * grace period ends, but not when lowering the possible feature level, as a lower level feature level should
+ * not make RRSIGs appear or OPT appear, but rather make them disappear. If the reappear anyway, then that's
+ * indication for a differently broken OPT/RRSIG implementation, and we really don't want to support that
+ * either.
+ *
+ * This is particularly important to deal with certain Belkin routers which break OPT for certain lookups (A),
+ * but pass traffic through for others (AAAA). If we detect the broken behaviour on one lookup we should not
+ * reenable it for another, because we cannot validate things anyway, given that the RRSIG/OPT data will be
+ * incomplete. */
+}
+
+DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) {
+ assert(s);
+
+ if (s->possible_feature_level != DNS_SERVER_FEATURE_LEVEL_BEST &&
+ dns_server_grace_period_expired(s)) {
+
+ s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_BEST;
+
+ dns_server_reset_counters(s);
+
+ s->packet_bad_opt = false;
+ s->packet_rrsig_missing = false;
+
+ log_info("Grace period over, resuming full feature set (%s) for DNS server %s.",
+ dns_server_feature_level_to_string(s->possible_feature_level),
+ dns_server_string(s));
+
+ } else if (s->possible_feature_level <= s->verified_feature_level)
+ s->possible_feature_level = s->verified_feature_level;
+ else {
+ DnsServerFeatureLevel p = s->possible_feature_level;
+
+ if (s->n_failed_tcp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS &&
+ s->possible_feature_level == DNS_SERVER_FEATURE_LEVEL_TCP) {
+
+ /* We are at the TCP (lowest) level, and we tried a couple of TCP connections, and it didn't
+ * work. Upgrade back to UDP again. */
+ log_debug("Reached maximum number of failed TCP connection attempts, trying UDP again...");
+ s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_UDP;
+
+ } else if (s->packet_bad_opt &&
+ s->possible_feature_level >= DNS_SERVER_FEATURE_LEVEL_EDNS0) {
+
+ /* A reply to one of our EDNS0 queries didn't carry a valid OPT RR, then downgrade to below
+ * EDNS0 levels. After all, some records generate different responses with and without OPT RR
+ * in the request. Example:
+ * https://open.nlnetlabs.nl/pipermail/dnssec-trigger/2014-November/000376.html */
+
+ log_debug("Server doesn't support EDNS(0) properly, downgrading feature level...");
+ s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_UDP;
+
+ } else if (s->packet_rrsig_missing &&
+ s->possible_feature_level >= DNS_SERVER_FEATURE_LEVEL_DO) {
+
+ /* RRSIG data was missing on a EDNS0 packet with DO bit set. This means the server doesn't
+ * augment responses with DNSSEC RRs. If so, let's better not ask the server for it anymore,
+ * after all some servers generate different replies depending if an OPT RR is in the query or
+ * not. */
+
+ log_debug("Detected server responses lack RRSIG records, downgrading feature level...");
+ s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_EDNS0;
+
+ } else if (s->n_failed_udp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS &&
+ s->possible_feature_level >= DNS_SERVER_FEATURE_LEVEL_UDP) {
+
+ /* We lost too many UDP packets in a row, and are on a feature level of UDP or higher. If the
+ * packets are lost, maybe the server cannot parse them, hence downgrading sounds like a good
+ * idea. We might downgrade all the way down to TCP this way. */
+
+ log_debug("Lost too many UDP packets, downgrading feature level...");
+ s->possible_feature_level--;
+
+ } else if (s->packet_failed &&
+ s->possible_feature_level > DNS_SERVER_FEATURE_LEVEL_UDP) {
+
+ /* We got a failure packet, and are at a feature level above UDP. Note that in this case we
+ * downgrade no further than UDP, under the assumption that a failure packet indicates an
+ * incompatible packet contents, but not a problem with the transport. */
+
+ log_debug("Got server failure, downgrading feature level...");
+ s->possible_feature_level--;
+
+ } else if (s->n_failed_tcp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS &&
+ s->packet_truncated &&
+ s->possible_feature_level > DNS_SERVER_FEATURE_LEVEL_UDP) {
+
+ /* We got too many TCP connection failures in a row, we had at least one truncated packet, and
+ * are on a feature level above UDP. By downgrading things and getting rid of DNSSEC or EDNS0
+ * data we hope to make the packet smaller, so that it still works via UDP given that TCP
+ * appears not to be a fallback. Note that if we are already at the lowest UDP level, we don't
+ * go further down, since that's TCP, and TCP failed too often after all. */
+
+ log_debug("Got too many failed TCP connection failures and truncated UDP packets, downgrading feature level...");
+ s->possible_feature_level--;
+ }
+
+ if (p != s->possible_feature_level) {
+
+ /* We changed the feature level, reset the counting */
+ dns_server_reset_counters(s);
+
+ log_warning("Using degraded feature set (%s) for DNS server %s.",
+ dns_server_feature_level_to_string(s->possible_feature_level),
+ dns_server_string(s));
+ }
+ }
+
+ return s->possible_feature_level;
+}
+
+int dns_server_adjust_opt(DnsServer *server, DnsPacket *packet, DnsServerFeatureLevel level) {
+ size_t packet_size;
+ bool edns_do;
+ int r;
+
+ assert(server);
+ assert(packet);
+ assert(packet->protocol == DNS_PROTOCOL_DNS);
+
+ /* Fix the OPT field in the packet to match our current feature level. */
+
+ r = dns_packet_truncate_opt(packet);
+ if (r < 0)
+ return r;
+
+ if (level < DNS_SERVER_FEATURE_LEVEL_EDNS0)
+ return 0;
+
+ edns_do = level >= DNS_SERVER_FEATURE_LEVEL_DO;
+
+ if (level >= DNS_SERVER_FEATURE_LEVEL_LARGE)
+ packet_size = DNS_PACKET_UNICAST_SIZE_LARGE_MAX;
+ else
+ packet_size = server->received_udp_packet_max;
+
+ return dns_packet_append_opt(packet, packet_size, edns_do, NULL);
+}
+
+const char *dns_server_string(DnsServer *server) {
+ assert(server);
+
+ if (!server->server_string)
+ (void) in_addr_to_string(server->family, &server->address, &server->server_string);
+
+ return strna(server->server_string);
+}
+
+bool dns_server_dnssec_supported(DnsServer *server) {
+ assert(server);
+
+ /* Returns whether the server supports DNSSEC according to what we know about it */
+
+ if (server->possible_feature_level < DNS_SERVER_FEATURE_LEVEL_DO)
+ return false;
+
+ if (server->packet_bad_opt)
+ return false;
+
+ if (server->packet_rrsig_missing)
+ return false;
+
+ /* DNSSEC servers need to support TCP properly (see RFC5966), if they don't, we assume DNSSEC is borked too */
+ if (server->n_failed_tcp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS)
+ return false;
+
+ return true;
+}
+
+static void dns_server_hash_func(const void *p, struct siphash *state) {
const DnsServer *s = p;
- uint64_t u;
- siphash24((uint8_t*) &u, &s->address, FAMILY_ADDRESS_SIZE(s->family), hash_key);
- u = u * hash_key[0] + u + s->family;
+ assert(s);
- return u;
+ siphash24_compress(&s->family, sizeof(s->family), state);
+ siphash24_compress(&s->address, FAMILY_ADDRESS_SIZE(s->family), state);
}
static int dns_server_compare_func(const void *a, const void *b) {
@@ -162,3 +571,145 @@ const struct hash_ops dns_server_hash_ops = {
.hash = dns_server_hash_func,
.compare = dns_server_compare_func
};
+
+void dns_server_unlink_all(DnsServer *first) {
+ DnsServer *next;
+
+ if (!first)
+ return;
+
+ next = first->servers_next;
+ dns_server_unlink(first);
+
+ dns_server_unlink_all(next);
+}
+
+void dns_server_unlink_marked(DnsServer *first) {
+ DnsServer *next;
+
+ if (!first)
+ return;
+
+ next = first->servers_next;
+
+ if (first->marked)
+ dns_server_unlink(first);
+
+ dns_server_unlink_marked(next);
+}
+
+void dns_server_mark_all(DnsServer *first) {
+ if (!first)
+ return;
+
+ first->marked = true;
+ dns_server_mark_all(first->servers_next);
+}
+
+DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr) {
+ DnsServer *s;
+
+ LIST_FOREACH(servers, s, first)
+ if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0)
+ return s;
+
+ return NULL;
+}
+
+DnsServer *manager_get_first_dns_server(Manager *m, DnsServerType t) {
+ assert(m);
+
+ switch (t) {
+
+ case DNS_SERVER_SYSTEM:
+ return m->dns_servers;
+
+ case DNS_SERVER_FALLBACK:
+ return m->fallback_dns_servers;
+
+ default:
+ return NULL;
+ }
+}
+
+DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) {
+ assert(m);
+
+ if (m->current_dns_server == s)
+ return s;
+
+ if (s)
+ log_info("Switching to system DNS server %s.", dns_server_string(s));
+
+ dns_server_unref(m->current_dns_server);
+ m->current_dns_server = dns_server_ref(s);
+
+ if (m->unicast_scope)
+ dns_cache_flush(&m->unicast_scope->cache);
+
+ return s;
+}
+
+DnsServer *manager_get_dns_server(Manager *m) {
+ Link *l;
+ assert(m);
+
+ /* Try to read updates resolv.conf */
+ manager_read_resolv_conf(m);
+
+ /* If no DNS server was chose so far, pick the first one */
+ if (!m->current_dns_server)
+ manager_set_dns_server(m, m->dns_servers);
+
+ if (!m->current_dns_server) {
+ bool found = false;
+ Iterator i;
+
+ /* No DNS servers configured, let's see if there are
+ * any on any links. If not, we use the fallback
+ * servers */
+
+ HASHMAP_FOREACH(l, m->links, i)
+ if (l->dns_servers) {
+ found = true;
+ break;
+ }
+
+ if (!found)
+ manager_set_dns_server(m, m->fallback_dns_servers);
+ }
+
+ return m->current_dns_server;
+}
+
+void manager_next_dns_server(Manager *m) {
+ assert(m);
+
+ /* If there's currently no DNS server set, then the next
+ * manager_get_dns_server() will find one */
+ if (!m->current_dns_server)
+ return;
+
+ /* Change to the next one, but make sure to follow the linked
+ * list only if the server is still linked. */
+ if (m->current_dns_server->linked && m->current_dns_server->servers_next) {
+ manager_set_dns_server(m, m->current_dns_server->servers_next);
+ return;
+ }
+
+ /* If there was no next one, then start from the beginning of
+ * the list */
+ if (m->current_dns_server->type == DNS_SERVER_FALLBACK)
+ manager_set_dns_server(m, m->fallback_dns_servers);
+ else
+ manager_set_dns_server(m, m->dns_servers);
+}
+
+static const char* const dns_server_feature_level_table[_DNS_SERVER_FEATURE_LEVEL_MAX] = {
+ [DNS_SERVER_FEATURE_LEVEL_TCP] = "TCP",
+ [DNS_SERVER_FEATURE_LEVEL_UDP] = "UDP",
+ [DNS_SERVER_FEATURE_LEVEL_EDNS0] = "UDP+EDNS0",
+ [DNS_SERVER_FEATURE_LEVEL_DO] = "UDP+EDNS0+DO",
+ [DNS_SERVER_FEATURE_LEVEL_LARGE] = "UDP+EDNS0+DO+LARGE",
+};
+DEFINE_STRING_TABLE_LOOKUP(dns_server_feature_level, DnsServerFeatureLevel);
diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h
index 10111fd6bd..02bd3463a7 100644
--- a/src/resolve/resolved-dns-server.h
+++ b/src/resolve/resolved-dns-server.h
@@ -24,7 +24,6 @@
#include "in-addr-util.h"
typedef struct DnsServer DnsServer;
-typedef enum DnsServerSource DnsServerSource;
typedef enum DnsServerType {
DNS_SERVER_SYSTEM,
@@ -32,7 +31,24 @@ typedef enum DnsServerType {
DNS_SERVER_LINK,
} DnsServerType;
+typedef enum DnsServerFeatureLevel {
+ DNS_SERVER_FEATURE_LEVEL_TCP,
+ DNS_SERVER_FEATURE_LEVEL_UDP,
+ DNS_SERVER_FEATURE_LEVEL_EDNS0,
+ DNS_SERVER_FEATURE_LEVEL_DO,
+ DNS_SERVER_FEATURE_LEVEL_LARGE,
+ _DNS_SERVER_FEATURE_LEVEL_MAX,
+ _DNS_SERVER_FEATURE_LEVEL_INVALID = -1
+} DnsServerFeatureLevel;
+
+#define DNS_SERVER_FEATURE_LEVEL_WORST 0
+#define DNS_SERVER_FEATURE_LEVEL_BEST (_DNS_SERVER_FEATURE_LEVEL_MAX - 1)
+
+const char* dns_server_feature_level_to_string(int i) _const_;
+int dns_server_feature_level_from_string(const char *s) _pure_;
+
#include "resolved-link.h"
+#include "resolved-manager.h"
struct DnsServer {
Manager *manager;
@@ -40,33 +56,80 @@ struct DnsServer {
unsigned n_ref;
DnsServerType type;
-
Link *link;
int family;
union in_addr_union address;
+ char *server_string;
+
usec_t resend_timeout;
usec_t max_rtt;
+ DnsServerFeatureLevel verified_feature_level;
+ DnsServerFeatureLevel possible_feature_level;
+
+ size_t received_udp_packet_max;
+
+ unsigned n_failed_udp;
+ unsigned n_failed_tcp;
+
+ bool packet_failed:1;
+ bool packet_truncated:1;
+ bool packet_bad_opt:1;
+ bool packet_rrsig_missing:1;
+
+ usec_t verified_usec;
+ usec_t features_grace_period_usec;
+
+ /* Used when GC'ing old DNS servers when configuration changes. */
bool marked:1;
+ /* If linked is set, then this server appears in the servers linked list */
+ bool linked:1;
LIST_FIELDS(DnsServer, servers);
};
int dns_server_new(
Manager *m,
- DnsServer **s,
+ DnsServer **ret,
DnsServerType type,
- Link *l,
+ Link *link,
int family,
const union in_addr_union *address);
DnsServer* dns_server_ref(DnsServer *s);
DnsServer* dns_server_unref(DnsServer *s);
-void dns_server_packet_received(DnsServer *s, usec_t rtt);
-void dns_server_packet_lost(DnsServer *s, usec_t usec);
+void dns_server_unlink(DnsServer *s);
+void dns_server_move_back_and_unmark(DnsServer *s);
+
+void dns_server_packet_received(DnsServer *s, int protocol, DnsServerFeatureLevel level, usec_t rtt, size_t size);
+void dns_server_packet_lost(DnsServer *s, int protocol, DnsServerFeatureLevel level, usec_t usec);
+void dns_server_packet_failed(DnsServer *s, DnsServerFeatureLevel level);
+void dns_server_packet_truncated(DnsServer *s, DnsServerFeatureLevel level);
+void dns_server_packet_rrsig_missing(DnsServer *s, DnsServerFeatureLevel level);
+void dns_server_packet_bad_opt(DnsServer *s, DnsServerFeatureLevel level);
+
+DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s);
+
+int dns_server_adjust_opt(DnsServer *server, DnsPacket *packet, DnsServerFeatureLevel level);
+
+const char *dns_server_string(DnsServer *server);
+
+bool dns_server_dnssec_supported(DnsServer *server);
+
+DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr);
+
+void dns_server_unlink_all(DnsServer *first);
+void dns_server_unlink_marked(DnsServer *first);
+void dns_server_mark_all(DnsServer *first);
+
+DnsServer *manager_get_first_dns_server(Manager *m, DnsServerType t);
+
+DnsServer *manager_set_dns_server(Manager *m, DnsServer *s);
+DnsServer *manager_get_dns_server(Manager *m);
+void manager_next_dns_server(Manager *m);
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsServer*, dns_server_unref);
diff --git a/src/resolve/resolved-dns-stream.c b/src/resolve/resolved-dns-stream.c
index 7f47e7223a..b72e6cc06f 100644
--- a/src/resolve/resolved-dns-stream.c
+++ b/src/resolve/resolved-dns-stream.c
@@ -21,6 +21,9 @@
#include <netinet/tcp.h>
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "io-util.h"
#include "missing.h"
#include "resolved-dns-stream.h"
@@ -344,7 +347,6 @@ DnsStream *dns_stream_free(DnsStream *s) {
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsStream*, dns_stream_free);
int dns_stream_new(Manager *m, DnsStream **ret, DnsProtocol protocol, int fd) {
- static const int one = 1;
_cleanup_(dns_stream_freep) DnsStream *s = NULL;
int r;
@@ -361,14 +363,12 @@ int dns_stream_new(Manager *m, DnsStream **ret, DnsProtocol protocol, int fd) {
s->fd = -1;
s->protocol = protocol;
- r = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
- if (r < 0)
- return -errno;
-
r = sd_event_add_io(m->event, &s->io_event_source, fd, EPOLLIN, on_stream_io, s);
if (r < 0)
return r;
+ (void) sd_event_source_set_description(s->io_event_source, "dns-stream-io");
+
r = sd_event_add_time(
m->event,
&s->timeout_event_source,
@@ -378,6 +378,8 @@ int dns_stream_new(Manager *m, DnsStream **ret, DnsProtocol protocol, int fd) {
if (r < 0)
return r;
+ (void) sd_event_source_set_description(s->timeout_event_source, "dns-stream-timeout");
+
LIST_PREPEND(streams, m->dns_streams, s);
s->manager = m;
s->fd = fd;
diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c
index b30473dd7e..5640cd1d33 100644
--- a/src/resolve/resolved-dns-transaction.c
+++ b/src/resolve/resolved-dns-transaction.c
@@ -19,48 +19,101 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "af-list.h"
+#include <sd-messages.h>
-#include "resolved-llmnr.h"
-#include "resolved-dns-transaction.h"
-#include "random-util.h"
+#include "af-list.h"
+#include "alloc-util.h"
#include "dns-domain.h"
+#include "fd-util.h"
+#include "random-util.h"
+#include "resolved-dns-cache.h"
+#include "resolved-dns-transaction.h"
+#include "resolved-llmnr.h"
+#include "string-table.h"
+
+#define TRANSACTIONS_MAX 4096
+
+static void dns_transaction_reset_answer(DnsTransaction *t) {
+ assert(t);
+
+ t->received = dns_packet_unref(t->received);
+ t->answer = dns_answer_unref(t->answer);
+ t->answer_rcode = 0;
+ t->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
+ t->answer_source = _DNS_TRANSACTION_SOURCE_INVALID;
+ t->answer_authenticated = false;
+ t->answer_nsec_ttl = (uint32_t) -1;
+}
+
+static void dns_transaction_flush_dnssec_transactions(DnsTransaction *t) {
+ DnsTransaction *z;
+
+ assert(t);
+
+ while ((z = set_steal_first(t->dnssec_transactions))) {
+ set_remove(z->notify_transactions, t);
+ dns_transaction_gc(z);
+ }
+}
+
+static void dns_transaction_close_connection(DnsTransaction *t) {
+ assert(t);
+
+ t->stream = dns_stream_free(t->stream);
+ t->dns_udp_event_source = sd_event_source_unref(t->dns_udp_event_source);
+ t->dns_udp_fd = safe_close(t->dns_udp_fd);
+}
+
+static void dns_transaction_stop_timeout(DnsTransaction *t) {
+ assert(t);
+
+ t->timeout_event_source = sd_event_source_unref(t->timeout_event_source);
+}
DnsTransaction* dns_transaction_free(DnsTransaction *t) {
- DnsQuery *q;
+ DnsQueryCandidate *c;
DnsZoneItem *i;
+ DnsTransaction *z;
if (!t)
return NULL;
- sd_event_source_unref(t->timeout_event_source);
+ log_debug("Freeing transaction %" PRIu16 ".", t->id);
- dns_packet_unref(t->sent);
- dns_packet_unref(t->received);
- dns_answer_unref(t->cached);
+ dns_transaction_close_connection(t);
+ dns_transaction_stop_timeout(t);
- sd_event_source_unref(t->dns_udp_event_source);
- safe_close(t->dns_udp_fd);
+ dns_packet_unref(t->sent);
+ dns_transaction_reset_answer(t);
dns_server_unref(t->server);
- dns_stream_free(t->stream);
if (t->scope) {
- hashmap_remove(t->scope->transactions, t->key);
+ hashmap_remove_value(t->scope->transactions_by_key, t->key, t);
+ LIST_REMOVE(transactions_by_scope, t->scope->transactions, t);
if (t->id != 0)
hashmap_remove(t->scope->manager->dns_transactions, UINT_TO_PTR(t->id));
}
- dns_resource_key_unref(t->key);
-
- while ((q = set_steal_first(t->queries)))
- set_remove(q->transactions, t);
- set_free(t->queries);
+ while ((c = set_steal_first(t->notify_query_candidates)))
+ set_remove(c->transactions, t);
+ set_free(t->notify_query_candidates);
- while ((i = set_steal_first(t->zone_items)))
+ while ((i = set_steal_first(t->notify_zone_items)))
i->probe_transaction = NULL;
- set_free(t->zone_items);
+ set_free(t->notify_zone_items);
+
+ while ((z = set_steal_first(t->notify_transactions)))
+ set_remove(z->dnssec_transactions, t);
+ set_free(t->notify_transactions);
+
+ dns_transaction_flush_dnssec_transactions(t);
+ set_free(t->dnssec_transactions);
+
+ dns_answer_unref(t->validated_keys);
+ dns_resource_key_unref(t->key);
+ free(t->key_string);
free(t);
return NULL;
@@ -68,14 +121,36 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) {
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsTransaction*, dns_transaction_free);
-void dns_transaction_gc(DnsTransaction *t) {
+bool dns_transaction_gc(DnsTransaction *t) {
assert(t);
if (t->block_gc > 0)
- return;
+ return true;
- if (set_isempty(t->queries) && set_isempty(t->zone_items))
+ if (set_isempty(t->notify_query_candidates) &&
+ set_isempty(t->notify_zone_items) &&
+ set_isempty(t->notify_transactions)) {
dns_transaction_free(t);
+ return false;
+ }
+
+ return true;
+}
+
+static uint16_t pick_new_id(Manager *m) {
+ uint16_t new_id;
+
+ /* Find a fresh, unused transaction id. Note that this loop is bounded because there's a limit on the number of
+ * transactions, and it's much lower than the space of IDs. */
+
+ assert_cc(TRANSACTIONS_MAX < 0xFFFF);
+
+ do
+ random_bytes(&new_id, sizeof(new_id));
+ while (new_id == 0 ||
+ hashmap_get(m->dns_transactions, UINT_TO_PTR(new_id)));
+
+ return new_id;
}
int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key) {
@@ -86,11 +161,24 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key)
assert(s);
assert(key);
+ /* Don't allow looking up invalid or pseudo RRs */
+ if (!dns_type_is_valid_query(key->type))
+ return -EINVAL;
+ if (dns_type_is_obsolete(key->type))
+ return -EOPNOTSUPP;
+
+ /* We only support the IN class */
+ if (key->class != DNS_CLASS_IN && key->class != DNS_CLASS_ANY)
+ return -EOPNOTSUPP;
+
+ if (hashmap_size(s->manager->dns_transactions) >= TRANSACTIONS_MAX)
+ return -EBUSY;
+
r = hashmap_ensure_allocated(&s->manager->dns_transactions, NULL);
if (r < 0)
return r;
- r = hashmap_ensure_allocated(&s->transactions, &dns_resource_key_hash_ops);
+ r = hashmap_ensure_allocated(&s->transactions_by_key, &dns_resource_key_hash_ops);
if (r < 0)
return r;
@@ -99,13 +187,13 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key)
return -ENOMEM;
t->dns_udp_fd = -1;
+ t->answer_source = _DNS_TRANSACTION_SOURCE_INVALID;
+ t->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
+ t->answer_nsec_ttl = (uint32_t) -1;
t->key = dns_resource_key_ref(key);
+ t->current_feature_level = _DNS_SERVER_FEATURE_LEVEL_INVALID;
- /* Find a fresh, unused transaction id */
- do
- random_bytes(&t->id, sizeof(t->id));
- while (t->id == 0 ||
- hashmap_get(s->manager->dns_transactions, UINT_TO_PTR(t->id)));
+ t->id = pick_new_id(s->manager);
r = hashmap_put(s->manager->dns_transactions, UINT_TO_PTR(t->id), t);
if (r < 0) {
@@ -113,14 +201,17 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key)
return r;
}
- r = hashmap_put(s->transactions, t->key, t);
+ r = hashmap_replace(s->transactions_by_key, t->key, t);
if (r < 0) {
hashmap_remove(s->manager->dns_transactions, UINT_TO_PTR(t->id));
return r;
}
+ LIST_PREPEND(transactions_by_scope, s->transactions, t);
t->scope = s;
+ s->manager->n_transactions_total ++;
+
if (ret)
*ret = t;
@@ -129,11 +220,20 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key)
return 0;
}
-static void dns_transaction_stop(DnsTransaction *t) {
+static void dns_transaction_shuffle_id(DnsTransaction *t) {
+ uint16_t new_id;
assert(t);
- t->timeout_event_source = sd_event_source_unref(t->timeout_event_source);
- t->stream = dns_stream_free(t->stream);
+ /* Pick a new ID for this transaction. */
+
+ new_id = pick_new_id(t->scope->manager);
+ assert_se(hashmap_remove_and_put(t->scope->manager->dns_transactions, UINT_TO_PTR(t->id), UINT_TO_PTR(new_id), t) >= 0);
+
+ log_debug("Transaction %" PRIu16 " is now %" PRIu16 ".", t->id, new_id);
+ t->id = new_id;
+
+ /* Make sure we generate a new packet with the new ID */
+ t->sent = dns_packet_unref(t->sent);
}
static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) {
@@ -148,7 +248,9 @@ static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) {
in_addr_to_string(p->family, &p->sender, &pretty);
- log_debug("Transaction on scope %s on %s/%s got tentative packet from %s",
+ log_debug("Transaction %" PRIu16 " for <%s> on scope %s on %s/%s got tentative packet from %s.",
+ t->id,
+ dns_transaction_key_string(t),
dns_protocol_to_string(t->scope->protocol),
t->scope->link ? t->scope->link->name : "*",
t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family),
@@ -164,7 +266,7 @@ static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) {
log_debug("We have the lexicographically larger IP address and thus lost in the conflict.");
t->block_gc++;
- while ((z = set_first(t->zone_items))) {
+ while ((z = set_first(t->notify_zone_items))) {
/* First, make sure the zone item drops the reference
* to us */
dns_zone_item_probe_stop(z);
@@ -179,39 +281,140 @@ static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) {
}
void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
- DnsQuery *q;
+ DnsQueryCandidate *c;
DnsZoneItem *z;
+ DnsTransaction *d;
Iterator i;
assert(t);
- assert(!IN_SET(state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
+ assert(!DNS_TRANSACTION_IS_LIVE(state));
+
+ if (state == DNS_TRANSACTION_DNSSEC_FAILED)
+ log_struct(LOG_NOTICE,
+ LOG_MESSAGE_ID(SD_MESSAGE_DNSSEC_FAILURE),
+ LOG_MESSAGE("DNSSEC validation failed for question %s: %s", dns_transaction_key_string(t), dnssec_result_to_string(t->answer_dnssec_result)),
+ "DNS_TRANSACTION=%" PRIu16, t->id,
+ "DNS_QUESTION=%s", dns_transaction_key_string(t),
+ "DNSSEC_RESULT=%s", dnssec_result_to_string(t->answer_dnssec_result),
+ NULL);
/* Note that this call might invalidate the query. Callers
* should hence not attempt to access the query or transaction
* after calling this function. */
- log_debug("Transaction on scope %s on %s/%s now complete with <%s>",
+ log_debug("Transaction %" PRIu16 " for <%s> on scope %s on %s/%s now complete with <%s> from %s (%s).",
+ t->id,
+ dns_transaction_key_string(t),
dns_protocol_to_string(t->scope->protocol),
t->scope->link ? t->scope->link->name : "*",
t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family),
- dns_transaction_state_to_string(state));
+ dns_transaction_state_to_string(state),
+ t->answer_source < 0 ? "none" : dns_transaction_source_to_string(t->answer_source),
+ t->answer_authenticated ? "authenticated" : "unsigned");
t->state = state;
- dns_transaction_stop(t);
+ dns_transaction_close_connection(t);
+ dns_transaction_stop_timeout(t);
/* Notify all queries that are interested, but make sure the
* transaction isn't freed while we are still looking at it */
t->block_gc++;
- SET_FOREACH(q, t->queries, i)
- dns_query_ready(q);
- SET_FOREACH(z, t->zone_items, i)
- dns_zone_item_ready(z);
- t->block_gc--;
+ SET_FOREACH(c, t->notify_query_candidates, i)
+ dns_query_candidate_notify(c);
+ SET_FOREACH(z, t->notify_zone_items, i)
+ dns_zone_item_notify(z);
+
+ if (!set_isempty(t->notify_transactions)) {
+ DnsTransaction **nt;
+ unsigned j, n = 0;
+
+ /* We need to be careful when notifying other
+ * transactions, as that might destroy other
+ * transactions in our list. Hence, in order to be
+ * able to safely iterate through the list of
+ * transactions, take a GC lock on all of them
+ * first. Then, in a second loop, notify them, but
+ * first unlock that specific transaction. */
+
+ nt = newa(DnsTransaction*, set_size(t->notify_transactions));
+ SET_FOREACH(d, t->notify_transactions, i) {
+ nt[n++] = d;
+ d->block_gc++;
+ }
+
+ assert(n == set_size(t->notify_transactions));
+
+ for (j = 0; j < n; j++) {
+ if (set_contains(t->notify_transactions, nt[j]))
+ dns_transaction_notify(nt[j], t);
+
+ nt[j]->block_gc--;
+ dns_transaction_gc(nt[j]);
+ }
+ }
+
+ t->block_gc--;
dns_transaction_gc(t);
}
+static int dns_transaction_pick_server(DnsTransaction *t) {
+ DnsServer *server;
+
+ assert(t);
+ assert(t->scope->protocol == DNS_PROTOCOL_DNS);
+
+ server = dns_scope_get_dns_server(t->scope);
+ if (!server)
+ return -ESRCH;
+
+ t->current_feature_level = dns_server_possible_feature_level(server);
+
+ if (server == t->server)
+ return 0;
+
+ dns_server_unref(t->server);
+ t->server = dns_server_ref(server);
+
+ return 1;
+}
+
+static void dns_transaction_retry(DnsTransaction *t) {
+ int r;
+
+ assert(t);
+
+ log_debug("Retrying transaction %" PRIu16 ".", t->id);
+
+ /* Before we try again, switch to a new server. */
+ dns_scope_next_dns_server(t->scope);
+
+ r = dns_transaction_go(t);
+ if (r < 0)
+ dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
+}
+
+static int dns_transaction_maybe_restart(DnsTransaction *t) {
+ assert(t);
+
+ if (!t->server)
+ return 0;
+
+ if (t->current_feature_level <= dns_server_possible_feature_level(t->server))
+ return 0;
+
+ /* The server's current feature level is lower than when we sent the original query. We learnt something from
+ the response or possibly an auxiliary DNSSEC response that we didn't know before. We take that as reason to
+ restart the whole transaction. This is a good idea to deal with servers that respond rubbish if we include
+ OPT RR or DO bit. One of these cases is documented here, for example:
+ https://open.nlnetlabs.nl/pipermail/dnssec-trigger/2014-November/000376.html */
+
+ log_debug("Server feature level is now lower than when we began our transaction. Restarting with new ID.");
+ dns_transaction_shuffle_id(t);
+ return dns_transaction_go(t);
+}
+
static int on_stream_complete(DnsStream *s, int error) {
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
DnsTransaction *t;
@@ -226,13 +429,23 @@ static int on_stream_complete(DnsStream *s, int error) {
t->stream = dns_stream_free(t->stream);
+ if (ERRNO_IS_DISCONNECT(error)) {
+ usec_t usec;
+
+ log_debug_errno(error, "Connection failure for DNS TCP stream: %m");
+ assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &usec) >= 0);
+ dns_server_packet_lost(t->server, IPPROTO_TCP, t->current_feature_level, usec - t->start_usec);
+
+ dns_transaction_retry(t);
+ return 0;
+ }
if (error != 0) {
dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
return 0;
}
if (dns_packet_validate_reply(p) <= 0) {
- log_debug("Invalid LLMNR TCP packet.");
+ log_debug("Invalid TCP reply packet.");
dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
return 0;
}
@@ -243,32 +456,46 @@ static int on_stream_complete(DnsStream *s, int error) {
dns_transaction_process_reply(t, p);
t->block_gc--;
- /* If the response wasn't useful, then complete the transition now */
+ /* If the response wasn't useful, then complete the transition
+ * now. After all, we are the worst feature set now with TCP
+ * sockets, and there's really no point in retrying. */
if (t->state == DNS_TRANSACTION_PENDING)
dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
+ else
+ dns_transaction_gc(t);
return 0;
}
static int dns_transaction_open_tcp(DnsTransaction *t) {
- DnsServer *server = NULL;
_cleanup_close_ int fd = -1;
int r;
assert(t);
- if (t->stream)
- return 0;
+ dns_transaction_close_connection(t);
switch (t->scope->protocol) {
+
case DNS_PROTOCOL_DNS:
- fd = dns_scope_tcp_socket(t->scope, AF_UNSPEC, NULL, 53, &server);
+ r = dns_transaction_pick_server(t);
+ if (r < 0)
+ return r;
+
+ if (!dns_server_dnssec_supported(t->server) && dns_type_is_dnssec(t->key->type))
+ return -EOPNOTSUPP;
+
+ r = dns_server_adjust_opt(t->server, t->sent, t->current_feature_level);
+ if (r < 0)
+ return r;
+
+ fd = dns_scope_socket_tcp(t->scope, AF_UNSPEC, NULL, t->server, 53);
break;
case DNS_PROTOCOL_LLMNR:
/* When we already received a reply to this (but it was truncated), send to its sender address */
if (t->received)
- fd = dns_scope_tcp_socket(t->scope, t->received->family, &t->received->sender, t->received->sender_port, NULL);
+ fd = dns_scope_socket_tcp(t->scope, t->received->family, &t->received->sender, NULL, t->received->sender_port);
else {
union in_addr_union address;
int family = AF_UNSPEC;
@@ -285,7 +512,7 @@ static int dns_transaction_open_tcp(DnsTransaction *t) {
if (family != t->scope->family)
return -ESRCH;
- fd = dns_scope_tcp_socket(t->scope, family, &address, LLMNR_PORT, NULL);
+ fd = dns_scope_socket_tcp(t->scope, family, &address, NULL, LLMNR_PORT);
}
break;
@@ -300,7 +527,6 @@ static int dns_transaction_open_tcp(DnsTransaction *t) {
r = dns_stream_new(t->scope->manager, &t->stream, t->scope->protocol, fd);
if (r < 0)
return r;
-
fd = -1;
r = dns_stream_write_packet(t->stream, t->sent);
@@ -309,9 +535,6 @@ static int dns_transaction_open_tcp(DnsTransaction *t) {
return r;
}
- dns_server_unref(t->server);
- t->server = dns_server_ref(server);
- t->received = dns_packet_unref(t->received);
t->stream->complete = on_stream_complete;
t->stream->transaction = t;
@@ -321,17 +544,165 @@ static int dns_transaction_open_tcp(DnsTransaction *t) {
if (t->scope->link)
t->stream->ifindex = t->scope->link->ifindex;
+ dns_transaction_reset_answer(t);
+
+ t->tried_stream = true;
+
return 0;
}
-static void dns_transaction_next_dns_server(DnsTransaction *t) {
+static void dns_transaction_cache_answer(DnsTransaction *t) {
assert(t);
- t->server = dns_server_unref(t->server);
- t->dns_udp_event_source = sd_event_source_unref(t->dns_udp_event_source);
- t->dns_udp_fd = safe_close(t->dns_udp_fd);
+ /* For mDNS we cache whenever we get the packet, rather than
+ * in each transaction. */
+ if (!IN_SET(t->scope->protocol, DNS_PROTOCOL_DNS, DNS_PROTOCOL_LLMNR))
+ return;
- dns_scope_next_dns_server(t->scope);
+ /* We never cache if this packet is from the local host, under
+ * the assumption that a locally running DNS server would
+ * cache this anyway, and probably knows better when to flush
+ * the cache then we could. */
+ if (!DNS_PACKET_SHALL_CACHE(t->received))
+ return;
+
+ dns_cache_put(&t->scope->cache,
+ t->key,
+ t->answer_rcode,
+ t->answer,
+ t->answer_authenticated,
+ t->answer_nsec_ttl,
+ 0,
+ t->received->family,
+ &t->received->sender);
+}
+
+static bool dns_transaction_dnssec_is_live(DnsTransaction *t) {
+ DnsTransaction *dt;
+ Iterator i;
+
+ assert(t);
+
+ SET_FOREACH(dt, t->dnssec_transactions, i)
+ if (DNS_TRANSACTION_IS_LIVE(dt->state))
+ return true;
+
+ return false;
+}
+
+static int dns_transaction_dnssec_ready(DnsTransaction *t) {
+ DnsTransaction *dt;
+ Iterator i;
+
+ assert(t);
+
+ /* Checks whether the auxiliary DNSSEC transactions of our transaction have completed, or are still
+ * ongoing. Returns 0, if we aren't ready for the DNSSEC validation, positive if we are. */
+
+ SET_FOREACH(dt, t->dnssec_transactions, i) {
+
+ switch (dt->state) {
+
+ case DNS_TRANSACTION_NULL:
+ case DNS_TRANSACTION_PENDING:
+ case DNS_TRANSACTION_VALIDATING:
+ /* Still ongoing */
+ return 0;
+
+ case DNS_TRANSACTION_RCODE_FAILURE:
+ if (dt->answer_rcode != DNS_RCODE_NXDOMAIN) {
+ log_debug("Auxiliary DNSSEC RR query failed with rcode=%s.", dns_rcode_to_string(dt->answer_rcode));
+ goto fail;
+ }
+
+ /* Fall-through: NXDOMAIN is good enough for us. This is because some DNS servers erronously
+ * return NXDOMAIN for empty non-terminals (Akamai...), and we need to handle that nicely, when
+ * asking for parent SOA or similar RRs to make unsigned proofs. */
+
+ case DNS_TRANSACTION_SUCCESS:
+ /* All good. */
+ break;
+
+ case DNS_TRANSACTION_DNSSEC_FAILED:
+ /* We handle DNSSEC failures different from other errors, as we care about the DNSSEC
+ * validationr result */
+
+ log_debug("Auxiliary DNSSEC RR query failed validation: %s", dnssec_result_to_string(dt->answer_dnssec_result));
+ t->answer_dnssec_result = dt->answer_dnssec_result; /* Copy error code over */
+ dns_transaction_complete(t, DNS_TRANSACTION_DNSSEC_FAILED);
+ return 0;
+
+
+ default:
+ log_debug("Auxiliary DNSSEC RR query failed with %s", dns_transaction_state_to_string(dt->state));
+ goto fail;
+ }
+ }
+
+ /* All is ready, we can go and validate */
+ return 1;
+
+fail:
+ t->answer_dnssec_result = DNSSEC_FAILED_AUXILIARY;
+ dns_transaction_complete(t, DNS_TRANSACTION_DNSSEC_FAILED);
+ return 0;
+}
+
+static void dns_transaction_process_dnssec(DnsTransaction *t) {
+ int r;
+
+ assert(t);
+
+ /* Are there ongoing DNSSEC transactions? If so, let's wait for them. */
+ r = dns_transaction_dnssec_ready(t);
+ if (r < 0) {
+ dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
+ return;
+ }
+ if (r == 0) /* We aren't ready yet (or one of our auxiliary transactions failed, and we shouldn't validate now */
+ return;
+
+ /* See if we learnt things from the additional DNSSEC transactions, that we didn't know before, and better
+ * restart the lookup immediately. */
+ r = dns_transaction_maybe_restart(t);
+ if (r < 0) {
+ dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
+ return;
+ }
+ if (r > 0) /* Transaction got restarted... */
+ return;
+
+ /* All our auxiliary DNSSEC transactions are complete now. Try
+ * to validate our RRset now. */
+ r = dns_transaction_validate_dnssec(t);
+ if (r < 0) {
+ dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
+ return;
+ }
+
+ if (t->answer_dnssec_result == DNSSEC_INCOMPATIBLE_SERVER &&
+ t->scope->dnssec_mode == DNSSEC_YES) {
+ /* We are not in automatic downgrade mode, and the
+ * server is bad, refuse operation. */
+ dns_transaction_complete(t, DNS_TRANSACTION_DNSSEC_FAILED);
+ return;
+ }
+
+ if (!IN_SET(t->answer_dnssec_result,
+ _DNSSEC_RESULT_INVALID, /* No DNSSEC validation enabled */
+ DNSSEC_VALIDATED, /* Answer is signed and validated successfully */
+ DNSSEC_UNSIGNED, /* Answer is right-fully unsigned */
+ DNSSEC_INCOMPATIBLE_SERVER)) { /* Server does not do DNSSEC (Yay, we are downgrade attack vulnerable!) */
+ dns_transaction_complete(t, DNS_TRANSACTION_DNSSEC_FAILED);
+ return;
+ }
+
+ dns_transaction_cache_answer(t);
+
+ if (t->answer_rcode == DNS_RCODE_SUCCESS)
+ dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
+ else
+ dns_transaction_complete(t, DNS_TRANSACTION_RCODE_FAILURE);
}
void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
@@ -340,15 +711,20 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
assert(t);
assert(p);
- assert(t->state == DNS_TRANSACTION_PENDING);
assert(t->scope);
assert(t->scope->manager);
+ if (t->state != DNS_TRANSACTION_PENDING)
+ return;
+
/* Note that this call might invalidate the query. Callers
* should hence not attempt to access the query or transaction
* after calling this function. */
+ log_debug("Processing incoming packet on transaction %" PRIu16".", t->id);
+
switch (t->scope->protocol) {
+
case DNS_PROTOCOL_LLMNR:
assert(t->scope->link);
@@ -371,7 +747,24 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
break;
+ case DNS_PROTOCOL_MDNS:
+ assert(t->scope->link);
+
+ /* For mDNS we will not accept any packets from other interfaces */
+ if (p->ifindex != t->scope->link->ifindex)
+ return;
+
+ if (p->family != t->scope->family)
+ return;
+
+ break;
+
case DNS_PROTOCOL_DNS:
+ /* Note that we do not need to verify the
+ * addresses/port numbers of incoming traffic, as we
+ * invoked connect() on our UDP socket in which case
+ * the kernel already does the needed verification for
+ * us. */
break;
default:
@@ -383,6 +776,8 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
t->received = dns_packet_ref(p);
}
+ t->answer_source = DNS_TRANSACTION_NETWORK;
+
if (p->ipproto == IPPROTO_TCP) {
if (DNS_PACKET_TC(p)) {
/* Truncated via TCP? Somebody must be fucking with us */
@@ -400,22 +795,42 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0);
switch (t->scope->protocol) {
+
case DNS_PROTOCOL_DNS:
assert(t->server);
- dns_server_packet_received(t->server, ts - t->start_usec);
+ if (IN_SET(DNS_PACKET_RCODE(p), DNS_RCODE_FORMERR, DNS_RCODE_SERVFAIL, DNS_RCODE_NOTIMP)) {
+
+ /* Request failed, immediately try again with reduced features */
+ log_debug("Server returned error: %s", dns_rcode_to_string(DNS_PACKET_RCODE(p)));
+
+ dns_server_packet_failed(t->server, t->current_feature_level);
+ dns_transaction_retry(t);
+ return;
+ } else if (DNS_PACKET_TC(p))
+ dns_server_packet_truncated(t->server, t->current_feature_level);
break;
+
case DNS_PROTOCOL_LLMNR:
case DNS_PROTOCOL_MDNS:
dns_scope_packet_received(t->scope, ts - t->start_usec);
-
break;
+
default:
- break;
+ assert_not_reached("Invalid DNS protocol.");
}
if (DNS_PACKET_TC(p)) {
+
+ /* Truncated packets for mDNS are not allowed. Give up immediately. */
+ if (t->scope->protocol == DNS_PROTOCOL_MDNS) {
+ dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
+ return;
+ }
+
+ log_debug("Reply truncated, retrying via TCP.");
+
/* Response was truncated, let's try again with good old TCP */
r = dns_transaction_open_tcp(t);
if (r == -ESRCH) {
@@ -423,47 +838,100 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS);
return;
}
+ if (r == -EOPNOTSUPP) {
+ /* Tried to ask for DNSSEC RRs, on a server that doesn't do DNSSEC */
+ dns_transaction_complete(t, DNS_TRANSACTION_RR_TYPE_UNSUPPORTED);
+ return;
+ }
if (r < 0) {
/* On LLMNR, if we cannot connect to the host,
* we immediately give up */
- if (t->scope->protocol == DNS_PROTOCOL_LLMNR) {
+ if (t->scope->protocol != DNS_PROTOCOL_DNS) {
dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
return;
}
/* On DNS, couldn't send? Try immediately again, with a new server */
- dns_transaction_next_dns_server(t);
-
- r = dns_transaction_go(t);
- if (r < 0) {
- dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
- return;
- }
-
- return;
+ dns_transaction_retry(t);
}
+
+ return;
}
- /* Parse and update the cache */
+ /* After the superficial checks, actually parse the message. */
r = dns_packet_extract(p);
if (r < 0) {
dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
return;
}
- /* Only consider responses with equivalent query section to the request */
- if (p->question->n_keys != 1 || dns_resource_key_equal(p->question->keys[0], t->key) <= 0) {
- dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
+ /* Report that the OPT RR was missing */
+ if (t->server) {
+ if (!p->opt)
+ dns_server_packet_bad_opt(t->server, t->current_feature_level);
+
+ dns_server_packet_received(t->server, p->ipproto, t->current_feature_level, ts - t->start_usec, p->size);
+ }
+
+ /* See if we know things we didn't know before that indicate we better restart the lookup immediately. */
+ r = dns_transaction_maybe_restart(t);
+ if (r < 0) {
+ dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
return;
}
+ if (r > 0) /* Transaction got restarted... */
+ return;
- /* According to RFC 4795, section 2.9. only the RRs from the answer section shall be cached */
- dns_cache_put(&t->scope->cache, t->key, DNS_PACKET_RCODE(p), p->answer, DNS_PACKET_ANCOUNT(p), 0, p->family, &p->sender);
+ if (IN_SET(t->scope->protocol, DNS_PROTOCOL_DNS, DNS_PROTOCOL_LLMNR)) {
- if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS)
- dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
- else
- dns_transaction_complete(t, DNS_TRANSACTION_FAILURE);
+ /* Only consider responses with equivalent query section to the request */
+ r = dns_packet_is_reply_for(p, t->key);
+ if (r < 0) {
+ dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
+ return;
+ }
+ if (r == 0) {
+ dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
+ return;
+ }
+
+ /* Install the answer as answer to the transaction */
+ dns_answer_unref(t->answer);
+ t->answer = dns_answer_ref(p->answer);
+ t->answer_rcode = DNS_PACKET_RCODE(p);
+ t->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
+ t->answer_authenticated = false;
+
+ /* Block GC while starting requests for additional DNSSEC RRs */
+ t->block_gc++;
+ r = dns_transaction_request_dnssec_keys(t);
+ t->block_gc--;
+
+ /* Maybe the transaction is ready for GC'ing now? If so, free it and return. */
+ if (!dns_transaction_gc(t))
+ return;
+
+ /* Requesting additional keys might have resulted in
+ * this transaction to fail, since the auxiliary
+ * request failed for some reason. If so, we are not
+ * in pending state anymore, and we should exit
+ * quickly. */
+ if (t->state != DNS_TRANSACTION_PENDING)
+ return;
+ if (r < 0) {
+ dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
+ return;
+ }
+ if (r > 0) {
+ /* There are DNSSEC transactions pending now. Update the state accordingly. */
+ t->state = DNS_TRANSACTION_VALIDATING;
+ dns_transaction_close_connection(t);
+ dns_transaction_stop_timeout(t);
+ return;
+ }
+ }
+
+ dns_transaction_process_dnssec(t);
}
static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
@@ -475,99 +943,125 @@ static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *use
assert(t->scope);
r = manager_recv(t->scope->manager, fd, DNS_PROTOCOL_DNS, &p);
- if (r <= 0)
- return r;
+ if (ERRNO_IS_DISCONNECT(-r)) {
+ usec_t usec;
- if (dns_packet_validate_reply(p) > 0 &&
- DNS_PACKET_ID(p) == t->id)
- dns_transaction_process_reply(t, p);
- else
- log_debug("Invalid DNS packet.");
+ /* UDP connection failure get reported via ICMP and then are possible delivered to us on the next
+ * recvmsg(). Treat this like a lost packet. */
+
+ log_debug_errno(r, "Connection failure for DNS UDP packet: %m");
+ assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &usec) >= 0);
+ dns_server_packet_lost(t->server, IPPROTO_UDP, t->current_feature_level, usec - t->start_usec);
+ dns_transaction_retry(t);
+ return 0;
+ }
+ if (r < 0) {
+ dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
+ return 0;
+ }
+
+ r = dns_packet_validate_reply(p);
+ if (r < 0) {
+ log_debug_errno(r, "Received invalid DNS packet as response, ignoring: %m");
+ return 0;
+ }
+ if (r == 0) {
+ log_debug("Received inappropriate DNS packet as response, ignoring.");
+ return 0;
+ }
+
+ if (DNS_PACKET_ID(p) != t->id) {
+ log_debug("Received packet with incorrect transaction ID, ignoring.");
+ return 0;
+ }
+
+ dns_transaction_process_reply(t, p);
return 0;
}
-static int dns_transaction_emit(DnsTransaction *t) {
+static int dns_transaction_emit_udp(DnsTransaction *t) {
int r;
assert(t);
- if (t->scope->protocol == DNS_PROTOCOL_DNS && !t->server) {
- DnsServer *server = NULL;
- _cleanup_close_ int fd = -1;
-
- fd = dns_scope_udp_dns_socket(t->scope, &server);
- if (fd < 0)
- return fd;
+ if (t->scope->protocol == DNS_PROTOCOL_DNS) {
- r = sd_event_add_io(t->scope->manager->event, &t->dns_udp_event_source, fd, EPOLLIN, on_dns_packet, t);
+ r = dns_transaction_pick_server(t);
if (r < 0)
return r;
- t->dns_udp_fd = fd;
- fd = -1;
- t->server = dns_server_ref(server);
- }
+ if (t->current_feature_level < DNS_SERVER_FEATURE_LEVEL_UDP)
+ return -EAGAIN;
- r = dns_scope_emit(t->scope, t->dns_udp_fd, t->sent);
- if (r < 0)
- return r;
+ if (!dns_server_dnssec_supported(t->server) && dns_type_is_dnssec(t->key->type))
+ return -EOPNOTSUPP;
- return 0;
-}
+ if (r > 0 || t->dns_udp_fd < 0) { /* Server changed, or no connection yet. */
+ int fd;
-static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdata) {
- DnsTransaction *t = userdata;
- int r;
+ dns_transaction_close_connection(t);
- assert(s);
- assert(t);
+ fd = dns_scope_socket_udp(t->scope, t->server, 53);
+ if (fd < 0)
+ return fd;
- /* Timeout reached? Try again, with a new server */
- dns_transaction_next_dns_server(t);
+ r = sd_event_add_io(t->scope->manager->event, &t->dns_udp_event_source, fd, EPOLLIN, on_dns_packet, t);
+ if (r < 0) {
+ safe_close(fd);
+ return r;
+ }
- /* ... and possibly increased timeout */
- if (t->server)
- dns_server_packet_lost(t->server, usec - t->start_usec);
- else
- dns_scope_packet_lost(t->scope, usec - t->start_usec);
+ (void) sd_event_source_set_description(t->dns_udp_event_source, "dns-transaction-udp");
+ t->dns_udp_fd = fd;
+ }
- r = dns_transaction_go(t);
+ r = dns_server_adjust_opt(t->server, t->sent, t->current_feature_level);
+ if (r < 0)
+ return r;
+ } else
+ dns_transaction_close_connection(t);
+
+ r = dns_scope_emit_udp(t->scope, t->dns_udp_fd, t->sent);
if (r < 0)
- dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
+ return r;
+
+ dns_transaction_reset_answer(t);
return 0;
}
-static int dns_transaction_make_packet(DnsTransaction *t) {
- _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
- int r;
+static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdata) {
+ DnsTransaction *t = userdata;
+ assert(s);
assert(t);
- if (t->sent)
- return 0;
+ if (!t->initial_jitter_scheduled || t->initial_jitter_elapsed) {
+ /* Timeout reached? Increase the timeout for the server used */
+ switch (t->scope->protocol) {
- r = dns_packet_new_query(&p, t->scope->protocol, 0);
- if (r < 0)
- return r;
+ case DNS_PROTOCOL_DNS:
+ assert(t->server);
+ dns_server_packet_lost(t->server, t->stream ? IPPROTO_TCP : IPPROTO_UDP, t->current_feature_level, usec - t->start_usec);
+ break;
- r = dns_scope_good_key(t->scope, t->key);
- if (r < 0)
- return r;
- if (r == 0)
- return -EDOM;
+ case DNS_PROTOCOL_LLMNR:
+ case DNS_PROTOCOL_MDNS:
+ dns_scope_packet_lost(t->scope, usec - t->start_usec);
+ break;
- r = dns_packet_append_key(p, t->key, NULL);
- if (r < 0)
- return r;
+ default:
+ assert_not_reached("Invalid DNS protocol.");
+ }
- DNS_PACKET_HEADER(p)->qdcount = htobe16(1);
- DNS_PACKET_HEADER(p)->id = t->id;
+ if (t->initial_jitter_scheduled)
+ t->initial_jitter_elapsed = true;
+ }
- t->sent = p;
- p = NULL;
+ log_debug("Timeout reached on transaction %" PRIu16 ".", t->id);
+ dns_transaction_retry(t);
return 0;
}
@@ -576,57 +1070,116 @@ static usec_t transaction_get_resend_timeout(DnsTransaction *t) {
assert(t->scope);
switch (t->scope->protocol) {
+
case DNS_PROTOCOL_DNS:
assert(t->server);
-
return t->server->resend_timeout;
- case DNS_PROTOCOL_LLMNR:
+
case DNS_PROTOCOL_MDNS:
+ assert(t->n_attempts > 0);
+ return (1 << (t->n_attempts - 1)) * USEC_PER_SEC;
+
+ case DNS_PROTOCOL_LLMNR:
return t->scope->resend_timeout;
+
default:
assert_not_reached("Invalid DNS protocol.");
}
}
-int dns_transaction_go(DnsTransaction *t) {
- bool had_stream;
- usec_t ts;
+static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) {
int r;
assert(t);
- had_stream = !!t->stream;
-
- dns_transaction_stop(t);
-
- log_debug("Excercising transaction on scope %s on %s/%s",
- dns_protocol_to_string(t->scope->protocol),
- t->scope->link ? t->scope->link->name : "*",
- t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family));
+ dns_transaction_stop_timeout(t);
if (t->n_attempts >= TRANSACTION_ATTEMPTS_MAX(t->scope->protocol)) {
dns_transaction_complete(t, DNS_TRANSACTION_ATTEMPTS_MAX_REACHED);
return 0;
}
- if (t->scope->protocol == DNS_PROTOCOL_LLMNR && had_stream) {
+ if (t->scope->protocol == DNS_PROTOCOL_LLMNR && t->tried_stream) {
/* If we already tried via a stream, then we don't
* retry on LLMNR. See RFC 4795, Section 2.7. */
dns_transaction_complete(t, DNS_TRANSACTION_ATTEMPTS_MAX_REACHED);
return 0;
}
- assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0);
-
t->n_attempts++;
t->start_usec = ts;
- t->received = dns_packet_unref(t->received);
- t->cached = dns_answer_unref(t->cached);
- t->cached_rcode = 0;
+
+ dns_transaction_reset_answer(t);
+ dns_transaction_flush_dnssec_transactions(t);
+
+ /* Check the trust anchor. Do so only on classic DNS, since DNSSEC does not apply otherwise. */
+ if (t->scope->protocol == DNS_PROTOCOL_DNS) {
+ r = dns_trust_anchor_lookup_positive(&t->scope->manager->trust_anchor, t->key, &t->answer);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ t->answer_rcode = DNS_RCODE_SUCCESS;
+ t->answer_source = DNS_TRANSACTION_TRUST_ANCHOR;
+ t->answer_authenticated = true;
+ dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
+ return 0;
+ }
+
+ if (dns_name_is_root(DNS_RESOURCE_KEY_NAME(t->key)) &&
+ t->key->type == DNS_TYPE_DS) {
+
+ /* Hmm, this is a request for the root DS? A
+ * DS RR doesn't exist in the root zone, and
+ * if our trust anchor didn't know it either,
+ * this means we cannot do any DNSSEC logic
+ * anymore. */
+
+ if (t->scope->dnssec_mode == DNSSEC_ALLOW_DOWNGRADE) {
+ /* We are in downgrade mode. In this
+ * case, synthesize an unsigned empty
+ * response, so that the any lookup
+ * depending on this one can continue
+ * assuming there was no DS, and hence
+ * the root zone was unsigned. */
+
+ t->answer_rcode = DNS_RCODE_SUCCESS;
+ t->answer_source = DNS_TRANSACTION_TRUST_ANCHOR;
+ t->answer_authenticated = false;
+ dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
+ } else
+ /* If we are not in downgrade mode,
+ * then fail the lookup, because we
+ * cannot reasonably answer it. There
+ * might be DS RRs, but we don't know
+ * them, and the DNS server won't tell
+ * them to us (and even if it would,
+ * we couldn't validate it and trust
+ * it). */
+ dns_transaction_complete(t, DNS_TRANSACTION_NO_TRUST_ANCHOR);
+
+ return 0;
+ }
+ }
+
+ /* Check the zone, but only if this transaction is not used
+ * for probing or verifying a zone item. */
+ if (set_isempty(t->notify_zone_items)) {
+
+ r = dns_zone_lookup(&t->scope->zone, t->key, &t->answer, NULL, NULL);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ t->answer_rcode = DNS_RCODE_SUCCESS;
+ t->answer_source = DNS_TRANSACTION_ZONE;
+ t->answer_authenticated = true;
+ dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
+ return 0;
+ }
+ }
/* Check the cache, but only if this transaction is not used
* for probing or verifying a zone item. */
- if (set_isempty(t->zone_items)) {
+ if (set_isempty(t->notify_zone_items)) {
/* Before trying the cache, let's make sure we figured out a
* server to use. Should this cause a change of server this
@@ -636,43 +1189,224 @@ int dns_transaction_go(DnsTransaction *t) {
/* Let's then prune all outdated entries */
dns_cache_prune(&t->scope->cache);
- r = dns_cache_lookup(&t->scope->cache, t->key, &t->cached_rcode, &t->cached);
+ r = dns_cache_lookup(&t->scope->cache, t->key, &t->answer_rcode, &t->answer, &t->answer_authenticated);
if (r < 0)
return r;
if (r > 0) {
- if (t->cached_rcode == DNS_RCODE_SUCCESS)
+ t->answer_source = DNS_TRANSACTION_CACHE;
+ if (t->answer_rcode == DNS_RCODE_SUCCESS)
dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
else
- dns_transaction_complete(t, DNS_TRANSACTION_FAILURE);
+ dns_transaction_complete(t, DNS_TRANSACTION_RCODE_FAILURE);
return 0;
}
}
- if (t->scope->protocol == DNS_PROTOCOL_LLMNR && !t->initial_jitter) {
- usec_t jitter;
+ return 1;
+}
+
+static int dns_transaction_make_packet_mdns(DnsTransaction *t) {
+
+ _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
+ bool add_known_answers = false;
+ DnsTransaction *other;
+ unsigned qdcount;
+ usec_t ts;
+ int r;
+
+ assert(t);
+ assert(t->scope->protocol == DNS_PROTOCOL_MDNS);
+
+ /* Discard any previously prepared packet, so we can start over and coalesce again */
+ t->sent = dns_packet_unref(t->sent);
+
+ r = dns_packet_new_query(&p, t->scope->protocol, 0, false);
+ if (r < 0)
+ return r;
+
+ r = dns_packet_append_key(p, t->key, NULL);
+ if (r < 0)
+ return r;
+
+ qdcount = 1;
+
+ if (dns_key_is_shared(t->key))
+ add_known_answers = true;
+
+ /*
+ * For mDNS, we want to coalesce as many open queries in pending transactions into one single
+ * query packet on the wire as possible. To achieve that, we iterate through all pending transactions
+ * in our current scope, and see whether their timing contraints allow them to be sent.
+ */
+
+ assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0);
+
+ LIST_FOREACH(transactions_by_scope, other, t->scope->transactions) {
+
+ /* Skip ourselves */
+ if (other == t)
+ continue;
+
+ if (other->state != DNS_TRANSACTION_PENDING)
+ continue;
+
+ if (other->next_attempt_after > ts)
+ continue;
+
+ if (qdcount >= UINT16_MAX)
+ break;
+
+ r = dns_packet_append_key(p, other->key, NULL);
+
+ /*
+ * If we can't stuff more questions into the packet, just give up.
+ * One of the 'other' transactions will fire later and take care of the rest.
+ */
+ if (r == -EMSGSIZE)
+ break;
+
+ if (r < 0)
+ return r;
+
+ r = dns_transaction_prepare(other, ts);
+ if (r <= 0)
+ continue;
+
+ ts += transaction_get_resend_timeout(other);
+
+ r = sd_event_add_time(
+ other->scope->manager->event,
+ &other->timeout_event_source,
+ clock_boottime_or_monotonic(),
+ ts, 0,
+ on_transaction_timeout, other);
+ if (r < 0)
+ return r;
+
+ (void) sd_event_source_set_description(t->timeout_event_source, "dns-transaction-timeout");
+
+ other->state = DNS_TRANSACTION_PENDING;
+ other->next_attempt_after = ts;
+
+ qdcount ++;
+
+ if (dns_key_is_shared(other->key))
+ add_known_answers = true;
+ }
+
+ DNS_PACKET_HEADER(p)->qdcount = htobe16(qdcount);
+
+ /* Append known answer section if we're asking for any shared record */
+ if (add_known_answers) {
+ r = dns_cache_export_shared_to_packet(&t->scope->cache, p);
+ if (r < 0)
+ return r;
+ }
+
+ t->sent = p;
+ p = NULL;
+
+ return 0;
+}
+
+static int dns_transaction_make_packet(DnsTransaction *t) {
+ _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
+ int r;
+
+ assert(t);
+
+ if (t->scope->protocol == DNS_PROTOCOL_MDNS)
+ return dns_transaction_make_packet_mdns(t);
+
+ if (t->sent)
+ return 0;
+
+ r = dns_packet_new_query(&p, t->scope->protocol, 0, t->scope->dnssec_mode != DNSSEC_NO);
+ if (r < 0)
+ return r;
+
+ r = dns_scope_good_key(t->scope, t->key);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EDOM;
+
+ r = dns_packet_append_key(p, t->key, NULL);
+ if (r < 0)
+ return r;
+
+ DNS_PACKET_HEADER(p)->qdcount = htobe16(1);
+ DNS_PACKET_HEADER(p)->id = t->id;
+
+ t->sent = p;
+ p = NULL;
+
+ return 0;
+}
+
+int dns_transaction_go(DnsTransaction *t) {
+ usec_t ts;
+ int r;
+
+ assert(t);
+
+ assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0);
+
+ r = dns_transaction_prepare(t, ts);
+ if (r <= 0)
+ return r;
+
+ log_debug("Excercising transaction %" PRIu16 " for <%s> on scope %s on %s/%s.",
+ t->id,
+ dns_transaction_key_string(t),
+ dns_protocol_to_string(t->scope->protocol),
+ t->scope->link ? t->scope->link->name : "*",
+ t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family));
+
+ if (!t->initial_jitter_scheduled &&
+ (t->scope->protocol == DNS_PROTOCOL_LLMNR ||
+ t->scope->protocol == DNS_PROTOCOL_MDNS)) {
+ usec_t jitter, accuracy;
/* RFC 4795 Section 2.7 suggests all queries should be
* delayed by a random time from 0 to JITTER_INTERVAL. */
- t->initial_jitter = true;
+ t->initial_jitter_scheduled = true;
random_bytes(&jitter, sizeof(jitter));
- jitter %= LLMNR_JITTER_INTERVAL_USEC;
+
+ switch (t->scope->protocol) {
+
+ case DNS_PROTOCOL_LLMNR:
+ jitter %= LLMNR_JITTER_INTERVAL_USEC;
+ accuracy = LLMNR_JITTER_INTERVAL_USEC;
+ break;
+
+ case DNS_PROTOCOL_MDNS:
+ jitter %= MDNS_JITTER_RANGE_USEC;
+ jitter += MDNS_JITTER_MIN_USEC;
+ accuracy = MDNS_JITTER_RANGE_USEC;
+ break;
+ default:
+ assert_not_reached("bad protocol");
+ }
r = sd_event_add_time(
t->scope->manager->event,
&t->timeout_event_source,
clock_boottime_or_monotonic(),
- ts + jitter,
- LLMNR_JITTER_INTERVAL_USEC,
+ ts + jitter, accuracy,
on_transaction_timeout, t);
if (r < 0)
return r;
+ (void) sd_event_source_set_description(t->timeout_event_source, "dns-transaction-timeout");
+
t->n_attempts = 0;
+ t->next_attempt_after = ts;
t->state = DNS_TRANSACTION_PENDING;
- log_debug("Delaying LLMNR transaction for " USEC_FMT "us.", jitter);
+ log_debug("Delaying %s transaction for " USEC_FMT "us.", dns_protocol_to_string(t->scope->protocol), jitter);
return 0;
}
@@ -696,44 +1430,1535 @@ int dns_transaction_go(DnsTransaction *t) {
* always be made via TCP on LLMNR */
r = dns_transaction_open_tcp(t);
} else {
- /* Try via UDP, and if that fails due to large size try via TCP */
- r = dns_transaction_emit(t);
+ /* Try via UDP, and if that fails due to large size or lack of
+ * support try via TCP */
+ r = dns_transaction_emit_udp(t);
if (r == -EMSGSIZE)
+ log_debug("Sending query via TCP since it is too large.");
+ if (r == -EAGAIN)
+ log_debug("Sending query via TCP since server doesn't support UDP.");
+ if (r == -EMSGSIZE || r == -EAGAIN)
r = dns_transaction_open_tcp(t);
}
+
if (r == -ESRCH) {
/* No servers to send this to? */
dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS);
return 0;
- } else if (r < 0) {
+ }
+ if (r == -EOPNOTSUPP) {
+ /* Tried to ask for DNSSEC RRs, on a server that doesn't do DNSSEC */
+ dns_transaction_complete(t, DNS_TRANSACTION_RR_TYPE_UNSUPPORTED);
+ return 0;
+ }
+ if (r < 0) {
if (t->scope->protocol != DNS_PROTOCOL_DNS) {
dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
return 0;
}
/* Couldn't send? Try immediately again, with a new server */
- dns_transaction_next_dns_server(t);
+ dns_scope_next_dns_server(t->scope);
return dns_transaction_go(t);
}
+ ts += transaction_get_resend_timeout(t);
+
r = sd_event_add_time(
t->scope->manager->event,
&t->timeout_event_source,
clock_boottime_or_monotonic(),
- ts + transaction_get_resend_timeout(t), 0,
+ ts, 0,
on_transaction_timeout, t);
if (r < 0)
return r;
+ (void) sd_event_source_set_description(t->timeout_event_source, "dns-transaction-timeout");
+
t->state = DNS_TRANSACTION_PENDING;
+ t->next_attempt_after = ts;
+
return 1;
}
+static int dns_transaction_find_cyclic(DnsTransaction *t, DnsTransaction *aux) {
+ DnsTransaction *n;
+ Iterator i;
+ int r;
+
+ assert(t);
+ assert(aux);
+
+ /* Try to find cyclic dependencies between transaction objects */
+
+ if (t == aux)
+ return 1;
+
+ SET_FOREACH(n, aux->dnssec_transactions, i) {
+ r = dns_transaction_find_cyclic(t, n);
+ if (r != 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int dns_transaction_add_dnssec_transaction(DnsTransaction *t, DnsResourceKey *key, DnsTransaction **ret) {
+ DnsTransaction *aux;
+ int r;
+
+ assert(t);
+ assert(ret);
+ assert(key);
+
+ aux = dns_scope_find_transaction(t->scope, key, true);
+ if (!aux) {
+ r = dns_transaction_new(&aux, t->scope, key);
+ if (r < 0)
+ return r;
+ } else {
+ if (set_contains(t->dnssec_transactions, aux)) {
+ *ret = aux;
+ return 0;
+ }
+
+ r = dns_transaction_find_cyclic(t, aux);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ log_debug("Detected potential cyclic dependency, refusing to add transaction %" PRIu16 " (%s) as dependency for %" PRIu16 " (%s).",
+ aux->id,
+ strna(dns_transaction_key_string(aux)),
+ t->id,
+ strna(dns_transaction_key_string(t)));
+ return -ELOOP;
+ }
+ }
+
+ r = set_ensure_allocated(&t->dnssec_transactions, NULL);
+ if (r < 0)
+ goto gc;
+
+ r = set_ensure_allocated(&aux->notify_transactions, NULL);
+ if (r < 0)
+ goto gc;
+
+ r = set_put(t->dnssec_transactions, aux);
+ if (r < 0)
+ goto gc;
+
+ r = set_put(aux->notify_transactions, t);
+ if (r < 0) {
+ (void) set_remove(t->dnssec_transactions, aux);
+ goto gc;
+ }
+
+ *ret = aux;
+ return 1;
+
+gc:
+ dns_transaction_gc(aux);
+ return r;
+}
+
+static int dns_transaction_request_dnssec_rr(DnsTransaction *t, DnsResourceKey *key) {
+ _cleanup_(dns_answer_unrefp) DnsAnswer *a = NULL;
+ DnsTransaction *aux;
+ int r;
+
+ assert(t);
+ assert(key);
+
+ /* Try to get the data from the trust anchor */
+ r = dns_trust_anchor_lookup_positive(&t->scope->manager->trust_anchor, key, &a);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ r = dns_answer_extend(&t->validated_keys, a);
+ if (r < 0)
+ return r;
+
+ return 0;
+ }
+
+ /* This didn't work, ask for it via the network/cache then. */
+ r = dns_transaction_add_dnssec_transaction(t, key, &aux);
+ if (r == -ELOOP) /* This would result in a cyclic dependency */
+ return 0;
+ if (r < 0)
+ return r;
+
+ if (aux->state == DNS_TRANSACTION_NULL) {
+ r = dns_transaction_go(aux);
+ if (r < 0)
+ return r;
+ }
+
+ return 1;
+}
+
+static int dns_transaction_has_positive_answer(DnsTransaction *t, DnsAnswerFlags *flags) {
+ int r;
+
+ assert(t);
+
+ /* Checks whether the answer is positive, i.e. either a direct
+ * answer to the question, or a CNAME/DNAME for it */
+
+ r = dns_answer_match_key(t->answer, t->key, flags);
+ if (r != 0)
+ return r;
+
+ r = dns_answer_find_cname_or_dname(t->answer, t->key, NULL, flags);
+ if (r != 0)
+ return r;
+
+ return false;
+}
+
+static int dns_transaction_negative_trust_anchor_lookup(DnsTransaction *t, const char *name) {
+ int r;
+
+ assert(t);
+
+ /* Check whether the specified name is in the the NTA
+ * database, either in the global one, or the link-local
+ * one. */
+
+ r = dns_trust_anchor_lookup_negative(&t->scope->manager->trust_anchor, name);
+ if (r != 0)
+ return r;
+
+ if (!t->scope->link)
+ return 0;
+
+ return set_contains(t->scope->link->dnssec_negative_trust_anchors, name);
+}
+
+static int dns_transaction_has_unsigned_negative_answer(DnsTransaction *t) {
+ int r;
+
+ assert(t);
+
+ /* Checks whether the answer is negative, and lacks NSEC/NSEC3
+ * RRs to prove it */
+
+ r = dns_transaction_has_positive_answer(t, NULL);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return false;
+
+ /* Is this key explicitly listed as a negative trust anchor?
+ * If so, it's nothing we need to care about */
+ r = dns_transaction_negative_trust_anchor_lookup(t, DNS_RESOURCE_KEY_NAME(t->key));
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return false;
+
+ /* The answer does not contain any RRs that match to the
+ * question. If so, let's see if there are any NSEC/NSEC3 RRs
+ * included. If not, the answer is unsigned. */
+
+ r = dns_answer_contains_nsec_or_nsec3(t->answer);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return false;
+
+ return true;
+}
+
+static int dns_transaction_is_primary_response(DnsTransaction *t, DnsResourceRecord *rr) {
+ int r;
+
+ assert(t);
+ assert(rr);
+
+ /* Check if the specified RR is the "primary" response,
+ * i.e. either matches the question precisely or is a
+ * CNAME/DNAME for it, or is any kind of NSEC/NSEC3 RR */
+
+ r = dns_resource_key_match_rr(t->key, rr, NULL);
+ if (r != 0)
+ return r;
+
+ r = dns_resource_key_match_cname_or_dname(t->key, rr->key, NULL);
+ if (r != 0)
+ return r;
+
+ if (rr->key->type == DNS_TYPE_NSEC3) {
+ const char *p;
+
+ p = DNS_RESOURCE_KEY_NAME(rr->key);
+ r = dns_name_parent(&p);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(t->key), p);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return true;
+ }
+ }
+
+ return rr->key->type == DNS_TYPE_NSEC;
+}
+
+static bool dns_transaction_dnssec_supported(DnsTransaction *t) {
+ assert(t);
+
+ /* Checks whether our transaction's DNS server is assumed to be compatible with DNSSEC. Returns false as soon
+ * as we changed our mind about a server, and now believe it is incompatible with DNSSEC. */
+
+ if (t->scope->protocol != DNS_PROTOCOL_DNS)
+ return false;
+
+ /* If we have picked no server, then we are working from the cache or some other source, and DNSSEC might well
+ * be supported, hence return true. */
+ if (!t->server)
+ return true;
+
+ if (t->current_feature_level < DNS_SERVER_FEATURE_LEVEL_DO)
+ return false;
+
+ return dns_server_dnssec_supported(t->server);
+}
+
+static bool dns_transaction_dnssec_supported_full(DnsTransaction *t) {
+ DnsTransaction *dt;
+ Iterator i;
+
+ assert(t);
+
+ /* Checks whether our transaction our any of the auxiliary transactions couldn't do DNSSEC. */
+
+ if (!dns_transaction_dnssec_supported(t))
+ return false;
+
+ SET_FOREACH(dt, t->dnssec_transactions, i)
+ if (!dns_transaction_dnssec_supported(dt))
+ return false;
+
+ return true;
+}
+
+int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
+ DnsResourceRecord *rr;
+
+ int r;
+
+ assert(t);
+
+ /*
+ * Retrieve all auxiliary RRs for the answer we got, so that
+ * we can verify signatures or prove that RRs are rightfully
+ * unsigned. Specifically:
+ *
+ * - For RRSIG we get the matching DNSKEY
+ * - For DNSKEY we get the matching DS
+ * - For unsigned SOA/NS we get the matching DS
+ * - For unsigned CNAME/DNAME/DS we get the parent SOA RR
+ * - For other unsigned RRs we get the matching SOA RR
+ * - For SOA/NS/DS queries with no matching response RRs, and no NSEC/NSEC3, the parent's SOA RR
+ * - For other queries with no matching response RRs, and no NSEC/NSEC3, the SOA RR
+ */
+
+ if (t->scope->dnssec_mode == DNSSEC_NO)
+ return 0;
+ if (t->answer_source != DNS_TRANSACTION_NETWORK)
+ return 0; /* We only need to validate stuff from the network */
+ if (!dns_transaction_dnssec_supported(t))
+ return 0; /* If we can't do DNSSEC anyway there's no point in geting the auxiliary RRs */
+
+ DNS_ANSWER_FOREACH(rr, t->answer) {
+
+ if (dns_type_is_pseudo(rr->key->type))
+ continue;
+
+ /* If this RR is in the negative trust anchor, we don't need to validate it. */
+ r = dns_transaction_negative_trust_anchor_lookup(t, DNS_RESOURCE_KEY_NAME(rr->key));
+ if (r < 0)
+ return r;
+ if (r > 0)
+ continue;
+
+ switch (rr->key->type) {
+
+ case DNS_TYPE_RRSIG: {
+ /* For each RRSIG we request the matching DNSKEY */
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *dnskey = NULL;
+
+ /* If this RRSIG is about a DNSKEY RR and the
+ * signer is the same as the owner, then we
+ * already have the DNSKEY, and we don't have
+ * to look for more. */
+ if (rr->rrsig.type_covered == DNS_TYPE_DNSKEY) {
+ r = dns_name_equal(rr->rrsig.signer, DNS_RESOURCE_KEY_NAME(rr->key));
+ if (r < 0)
+ return r;
+ if (r > 0)
+ continue;
+ }
+
+ /* If the signer is not a parent of our
+ * original query, then this is about an
+ * auxiliary RRset, but not anything we asked
+ * for. In this case we aren't interested,
+ * because we don't want to request additional
+ * RRs for stuff we didn't really ask for, and
+ * also to avoid request loops, where
+ * additional RRs from one transaction result
+ * in another transaction whose additonal RRs
+ * point back to the original transaction, and
+ * we deadlock. */
+ r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(t->key), rr->rrsig.signer);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ dnskey = dns_resource_key_new(rr->key->class, DNS_TYPE_DNSKEY, rr->rrsig.signer);
+ if (!dnskey)
+ return -ENOMEM;
+
+ log_debug("Requesting DNSKEY to validate transaction %" PRIu16" (%s, RRSIG with key tag: %" PRIu16 ").", t->id, DNS_RESOURCE_KEY_NAME(rr->key), rr->rrsig.key_tag);
+ r = dns_transaction_request_dnssec_rr(t, dnskey);
+ if (r < 0)
+ return r;
+ break;
+ }
+
+ case DNS_TYPE_DNSKEY: {
+ /* For each DNSKEY we request the matching DS */
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *ds = NULL;
+
+ /* If the DNSKEY we are looking at is not for
+ * zone we are interested in, nor any of its
+ * parents, we aren't interested, and don't
+ * request it. After all, we don't want to end
+ * up in request loops, and want to keep
+ * additional traffic down. */
+
+ r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(t->key), DNS_RESOURCE_KEY_NAME(rr->key));
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ ds = dns_resource_key_new(rr->key->class, DNS_TYPE_DS, DNS_RESOURCE_KEY_NAME(rr->key));
+ if (!ds)
+ return -ENOMEM;
+
+ log_debug("Requesting DS to validate transaction %" PRIu16" (%s, DNSKEY with key tag: %" PRIu16 ").", t->id, DNS_RESOURCE_KEY_NAME(rr->key), dnssec_keytag(rr, false));
+ r = dns_transaction_request_dnssec_rr(t, ds);
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
+ case DNS_TYPE_SOA:
+ case DNS_TYPE_NS: {
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *ds = NULL;
+
+ /* For an unsigned SOA or NS, try to acquire
+ * the matching DS RR, as we are at a zone cut
+ * then, and whether a DS exists tells us
+ * whether the zone is signed. Do so only if
+ * this RR matches our original question,
+ * however. */
+
+ r = dns_resource_key_match_rr(t->key, rr, NULL);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ r = dnssec_has_rrsig(t->answer, rr->key);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ continue;
+
+ ds = dns_resource_key_new(rr->key->class, DNS_TYPE_DS, DNS_RESOURCE_KEY_NAME(rr->key));
+ if (!ds)
+ return -ENOMEM;
+
+ log_debug("Requesting DS to validate transaction %" PRIu16 " (%s, unsigned SOA/NS RRset).", t->id, DNS_RESOURCE_KEY_NAME(rr->key));
+ r = dns_transaction_request_dnssec_rr(t, ds);
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
+ case DNS_TYPE_DS:
+ case DNS_TYPE_CNAME:
+ case DNS_TYPE_DNAME: {
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *soa = NULL;
+ const char *name;
+
+ /* CNAMEs and DNAMEs cannot be located at a
+ * zone apex, hence ask for the parent SOA for
+ * unsigned CNAME/DNAME RRs, maybe that's the
+ * apex. But do all that only if this is
+ * actually a response to our original
+ * question.
+ *
+ * Similar for DS RRs, which are signed when
+ * the parent SOA is signed. */
+
+ r = dns_transaction_is_primary_response(t, rr);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ r = dnssec_has_rrsig(t->answer, rr->key);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ continue;
+
+ r = dns_answer_has_dname_for_cname(t->answer, rr);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ continue;
+
+ name = DNS_RESOURCE_KEY_NAME(rr->key);
+ r = dns_name_parent(&name);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ soa = dns_resource_key_new(rr->key->class, DNS_TYPE_SOA, name);
+ if (!soa)
+ return -ENOMEM;
+
+ log_debug("Requesting parent SOA to validate transaction %" PRIu16 " (%s, unsigned CNAME/DNAME/DS RRset).", t->id, DNS_RESOURCE_KEY_NAME(rr->key));
+ r = dns_transaction_request_dnssec_rr(t, soa);
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
+ default: {
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *soa = NULL;
+
+ /* For other unsigned RRsets (including
+ * NSEC/NSEC3!), look for proof the zone is
+ * unsigned, by requesting the SOA RR of the
+ * zone. However, do so only if they are
+ * directly relevant to our original
+ * question. */
+
+ r = dns_transaction_is_primary_response(t, rr);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ r = dnssec_has_rrsig(t->answer, rr->key);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ continue;
+
+ soa = dns_resource_key_new(rr->key->class, DNS_TYPE_SOA, DNS_RESOURCE_KEY_NAME(rr->key));
+ if (!soa)
+ return -ENOMEM;
+
+ log_debug("Requesting SOA to validate transaction %" PRIu16 " (%s, unsigned non-SOA/NS RRset <%s>).", t->id, DNS_RESOURCE_KEY_NAME(rr->key), dns_resource_record_to_string(rr));
+ r = dns_transaction_request_dnssec_rr(t, soa);
+ if (r < 0)
+ return r;
+ break;
+ }}
+ }
+
+ /* Above, we requested everything necessary to validate what
+ * we got. Now, let's request what we need to validate what we
+ * didn't get... */
+
+ r = dns_transaction_has_unsigned_negative_answer(t);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ const char *name;
+
+ name = DNS_RESOURCE_KEY_NAME(t->key);
+
+ /* If this was a SOA or NS request, then this
+ * indicates that we are not at a zone apex, hence ask
+ * the parent name instead. If this was a DS request,
+ * then it's signed when the parent zone is signed,
+ * hence ask the parent in that case, too. */
+
+ if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS, DNS_TYPE_DS)) {
+ r = dns_name_parent(&name);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ log_debug("Requesting parent SOA to validate transaction %" PRIu16 " (%s, unsigned empty SOA/NS/DS response).", t->id, DNS_RESOURCE_KEY_NAME(t->key));
+ else
+ name = NULL;
+ } else
+ log_debug("Requesting SOA to validate transaction %" PRIu16 " (%s, unsigned empty non-SOA/NS/DS response).", t->id, DNS_RESOURCE_KEY_NAME(t->key));
+
+ if (name) {
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *soa = NULL;
+
+ soa = dns_resource_key_new(t->key->class, DNS_TYPE_SOA, name);
+ if (!soa)
+ return -ENOMEM;
+
+ r = dns_transaction_request_dnssec_rr(t, soa);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ return dns_transaction_dnssec_is_live(t);
+}
+
+void dns_transaction_notify(DnsTransaction *t, DnsTransaction *source) {
+ assert(t);
+ assert(source);
+
+ /* Invoked whenever any of our auxiliary DNSSEC transactions completed its work. If the state is still PENDING,
+ we are still in the loop that adds further DNSSEC transactions, hence don't check if we are ready yet. If
+ the state is VALIDATING however, we should check if we are complete now. */
+
+ if (t->state == DNS_TRANSACTION_VALIDATING)
+ dns_transaction_process_dnssec(t);
+}
+
+static int dns_transaction_validate_dnskey_by_ds(DnsTransaction *t) {
+ DnsResourceRecord *rr;
+ int ifindex, r;
+
+ assert(t);
+
+ /* Add all DNSKEY RRs from the answer that are validated by DS
+ * RRs from the list of validated keys to the list of
+ * validated keys. */
+
+ DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, t->answer) {
+
+ r = dnssec_verify_dnskey_by_ds_search(rr, t->validated_keys);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ /* If so, the DNSKEY is validated too. */
+ r = dns_answer_add_extend(&t->validated_keys, rr, ifindex, DNS_ANSWER_AUTHENTICATED);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord *rr) {
+ int r;
+
+ assert(t);
+ assert(rr);
+
+ /* Checks if the RR we are looking for must be signed with an
+ * RRSIG. This is used for positive responses. */
+
+ if (t->scope->dnssec_mode == DNSSEC_NO)
+ return false;
+
+ if (dns_type_is_pseudo(rr->key->type))
+ return -EINVAL;
+
+ r = dns_transaction_negative_trust_anchor_lookup(t, DNS_RESOURCE_KEY_NAME(rr->key));
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return false;
+
+ switch (rr->key->type) {
+
+ case DNS_TYPE_RRSIG:
+ /* RRSIGs are the signatures themselves, they need no signing. */
+ return false;
+
+ case DNS_TYPE_SOA:
+ case DNS_TYPE_NS: {
+ DnsTransaction *dt;
+ Iterator i;
+
+ /* For SOA or NS RRs we look for a matching DS transaction */
+
+ SET_FOREACH(dt, t->dnssec_transactions, i) {
+
+ if (dt->key->class != rr->key->class)
+ continue;
+ if (dt->key->type != DNS_TYPE_DS)
+ continue;
+
+ r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dt->key), DNS_RESOURCE_KEY_NAME(rr->key));
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ /* We found a DS transactions for the SOA/NS
+ * RRs we are looking at. If it discovered signed DS
+ * RRs, then we need to be signed, too. */
+
+ if (!dt->answer_authenticated)
+ return false;
+
+ return dns_answer_match_key(dt->answer, dt->key, NULL);
+ }
+
+ /* We found nothing that proves this is safe to leave
+ * this unauthenticated, hence ask inist on
+ * authentication. */
+ return true;
+ }
+
+ case DNS_TYPE_DS:
+ case DNS_TYPE_CNAME:
+ case DNS_TYPE_DNAME: {
+ const char *parent = NULL;
+ DnsTransaction *dt;
+ Iterator i;
+
+ /*
+ * CNAME/DNAME RRs cannot be located at a zone apex, hence look directly for the parent SOA.
+ *
+ * DS RRs are signed if the parent is signed, hence also look at the parent SOA
+ */
+
+ SET_FOREACH(dt, t->dnssec_transactions, i) {
+
+ if (dt->key->class != rr->key->class)
+ continue;
+ if (dt->key->type != DNS_TYPE_SOA)
+ continue;
+
+ if (!parent) {
+ parent = DNS_RESOURCE_KEY_NAME(rr->key);
+ r = dns_name_parent(&parent);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ if (rr->key->type == DNS_TYPE_DS)
+ return true;
+
+ /* A CNAME/DNAME without a parent? That's sooo weird. */
+ log_debug("Transaction %" PRIu16 " claims CNAME/DNAME at root. Refusing.", t->id);
+ return -EBADMSG;
+ }
+ }
+
+ r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dt->key), parent);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ return t->answer_authenticated;
+ }
+
+ return true;
+ }
+
+ default: {
+ DnsTransaction *dt;
+ Iterator i;
+
+ /* Any other kind of RR (including DNSKEY/NSEC/NSEC3). Let's see if our SOA lookup was authenticated */
+
+ SET_FOREACH(dt, t->dnssec_transactions, i) {
+
+ if (dt->key->class != rr->key->class)
+ continue;
+ if (dt->key->type != DNS_TYPE_SOA)
+ continue;
+
+ r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dt->key), DNS_RESOURCE_KEY_NAME(rr->key));
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ /* We found the transaction that was supposed to find
+ * the SOA RR for us. It was successful, but found no
+ * RR for us. This means we are not at a zone cut. In
+ * this case, we require authentication if the SOA
+ * lookup was authenticated too. */
+ return t->answer_authenticated;
+ }
+
+ return true;
+ }}
+}
+
+static int dns_transaction_in_private_tld(DnsTransaction *t, const DnsResourceKey *key) {
+ DnsTransaction *dt;
+ const char *tld;
+ Iterator i;
+ int r;
+
+ /* If DNSSEC downgrade mode is on, checks whether the
+ * specified RR is one level below a TLD we have proven not to
+ * exist. In such a case we assume that this is a private
+ * domain, and permit it.
+ *
+ * This detects cases like the Fritz!Box router networks. Each
+ * Fritz!Box router serves a private "fritz.box" zone, in the
+ * non-existing TLD "box". Requests for the "fritz.box" domain
+ * are served by the router itself, while requests for the
+ * "box" domain will result in NXDOMAIN.
+ *
+ * Note that this logic is unable to detect cases where a
+ * router serves a private DNS zone directly under
+ * non-existing TLD. In such a case we cannot detect whether
+ * the TLD is supposed to exist or not, as all requests we
+ * make for it will be answered by the router's zone, and not
+ * by the root zone. */
+
+ assert(t);
+
+ if (t->scope->dnssec_mode != DNSSEC_ALLOW_DOWNGRADE)
+ return false; /* In strict DNSSEC mode what doesn't exist, doesn't exist */
+
+ tld = DNS_RESOURCE_KEY_NAME(key);
+ r = dns_name_parent(&tld);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return false; /* Already the root domain */
+
+ if (!dns_name_is_single_label(tld))
+ return false;
+
+ SET_FOREACH(dt, t->dnssec_transactions, i) {
+
+ if (dt->key->class != key->class)
+ continue;
+
+ r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dt->key), tld);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ /* We found an auxiliary lookup we did for the TLD. If
+ * that returned with NXDOMAIN, we know the TLD didn't
+ * exist, and hence this might be a private zone. */
+
+ return dt->answer_rcode == DNS_RCODE_NXDOMAIN;
+ }
+
+ return false;
+}
+
+static int dns_transaction_requires_nsec(DnsTransaction *t) {
+ DnsTransaction *dt;
+ const char *name;
+ Iterator i;
+ int r;
+
+ assert(t);
+
+ /* Checks if we need to insist on NSEC/NSEC3 RRs for proving
+ * this negative reply */
+
+ if (t->scope->dnssec_mode == DNSSEC_NO)
+ return false;
+
+ if (dns_type_is_pseudo(t->key->type))
+ return -EINVAL;
+
+ r = dns_transaction_negative_trust_anchor_lookup(t, DNS_RESOURCE_KEY_NAME(t->key));
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return false;
+
+ r = dns_transaction_in_private_tld(t, t->key);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ /* The lookup is from a TLD that is proven not to
+ * exist, and we are in downgrade mode, hence ignore
+ * that fact that we didn't get any NSEC RRs.*/
+
+ log_info("Detected a negative query %s in a private DNS zone, permitting unsigned response.", dns_transaction_key_string(t));
+ return false;
+ }
+
+ name = DNS_RESOURCE_KEY_NAME(t->key);
+
+ if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS, DNS_TYPE_DS)) {
+
+ /* We got a negative reply for this SOA/NS lookup? If
+ * so, then we are not at a zone apex, and thus should
+ * look at the result of the parent SOA lookup.
+ *
+ * We got a negative reply for this DS lookup? DS RRs
+ * are signed when their parent zone is signed, hence
+ * also check the parent SOA in this case. */
+
+ r = dns_name_parent(&name);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return true;
+ }
+
+ /* For all other RRs we check the SOA on the same level to see
+ * if it's signed. */
+
+ SET_FOREACH(dt, t->dnssec_transactions, i) {
+
+ if (dt->key->class != t->key->class)
+ continue;
+ if (dt->key->type != DNS_TYPE_SOA)
+ continue;
+
+ r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dt->key), name);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ return dt->answer_authenticated;
+ }
+
+ /* If in doubt, require NSEC/NSEC3 */
+ return true;
+}
+
+static int dns_transaction_dnskey_authenticated(DnsTransaction *t, DnsResourceRecord *rr) {
+ DnsResourceRecord *rrsig;
+ bool found = false;
+ int r;
+
+ /* Checks whether any of the DNSKEYs used for the RRSIGs for
+ * the specified RRset is authenticated (i.e. has a matching
+ * DS RR). */
+
+ r = dns_transaction_negative_trust_anchor_lookup(t, DNS_RESOURCE_KEY_NAME(rr->key));
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return false;
+
+ DNS_ANSWER_FOREACH(rrsig, t->answer) {
+ DnsTransaction *dt;
+ Iterator i;
+
+ r = dnssec_key_match_rrsig(rr->key, rrsig);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ SET_FOREACH(dt, t->dnssec_transactions, i) {
+
+ if (dt->key->class != rr->key->class)
+ continue;
+
+ if (dt->key->type == DNS_TYPE_DNSKEY) {
+
+ r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dt->key), rrsig->rrsig.signer);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ /* OK, we found an auxiliary DNSKEY
+ * lookup. If that lookup is
+ * authenticated, report this. */
+
+ if (dt->answer_authenticated)
+ return true;
+
+ found = true;
+
+ } else if (dt->key->type == DNS_TYPE_DS) {
+
+ r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dt->key), rrsig->rrsig.signer);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ /* OK, we found an auxiliary DS
+ * lookup. If that lookup is
+ * authenticated and non-zero, we
+ * won! */
+
+ if (!dt->answer_authenticated)
+ return false;
+
+ return dns_answer_match_key(dt->answer, dt->key, NULL);
+ }
+ }
+ }
+
+ return found ? false : -ENXIO;
+}
+
+static int dns_transaction_known_signed(DnsTransaction *t, DnsResourceRecord *rr) {
+ assert(t);
+ assert(rr);
+
+ /* We know that the root domain is signed, hence if it appears
+ * not to be signed, there's a problem with the DNS server */
+
+ return rr->key->class == DNS_CLASS_IN &&
+ dns_name_is_root(DNS_RESOURCE_KEY_NAME(rr->key));
+}
+
+static int dns_transaction_check_revoked_trust_anchors(DnsTransaction *t) {
+ DnsResourceRecord *rr;
+ int r;
+
+ assert(t);
+
+ /* Maybe warn the user that we encountered a revoked DNSKEY
+ * for a key from our trust anchor. Note that we don't care
+ * whether the DNSKEY can be authenticated or not. It's
+ * sufficient if it is self-signed. */
+
+ DNS_ANSWER_FOREACH(rr, t->answer) {
+ r = dns_trust_anchor_check_revoked(&t->scope->manager->trust_anchor, rr, t->answer);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int dns_transaction_invalidate_revoked_keys(DnsTransaction *t) {
+ bool changed;
+ int r;
+
+ assert(t);
+
+ /* Removes all DNSKEY/DS objects from t->validated_keys that
+ * our trust anchors database considers revoked. */
+
+ do {
+ DnsResourceRecord *rr;
+
+ changed = false;
+
+ DNS_ANSWER_FOREACH(rr, t->validated_keys) {
+ r = dns_trust_anchor_is_revoked(&t->scope->manager->trust_anchor, rr);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ r = dns_answer_remove_by_rr(&t->validated_keys, rr);
+ if (r < 0)
+ return r;
+
+ assert(r > 0);
+ changed = true;
+ break;
+ }
+ }
+ } while (changed);
+
+ return 0;
+}
+
+static int dns_transaction_copy_validated(DnsTransaction *t) {
+ DnsTransaction *dt;
+ Iterator i;
+ int r;
+
+ assert(t);
+
+ /* Copy all validated RRs from the auxiliary DNSSEC transactions into our set of validated RRs */
+
+ SET_FOREACH(dt, t->dnssec_transactions, i) {
+
+ if (DNS_TRANSACTION_IS_LIVE(dt->state))
+ continue;
+
+ if (!dt->answer_authenticated)
+ continue;
+
+ r = dns_answer_extend(&t->validated_keys, dt->answer);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int dns_transaction_validate_dnssec(DnsTransaction *t) {
+ _cleanup_(dns_answer_unrefp) DnsAnswer *validated = NULL;
+ enum {
+ PHASE_DNSKEY, /* Phase #1, only validate DNSKEYs */
+ PHASE_NSEC, /* Phase #2, only validate NSEC+NSEC3 */
+ PHASE_ALL, /* Phase #3, validate everything else */
+ } phase;
+ DnsResourceRecord *rr;
+ DnsAnswerFlags flags;
+ int r;
+
+ assert(t);
+
+ /* We have now collected all DS and DNSKEY RRs in
+ * t->validated_keys, let's see which RRs we can now
+ * authenticate with that. */
+
+ if (t->scope->dnssec_mode == DNSSEC_NO)
+ return 0;
+
+ /* Already validated */
+ if (t->answer_dnssec_result != _DNSSEC_RESULT_INVALID)
+ return 0;
+
+ /* Our own stuff needs no validation */
+ if (IN_SET(t->answer_source, DNS_TRANSACTION_ZONE, DNS_TRANSACTION_TRUST_ANCHOR)) {
+ t->answer_dnssec_result = DNSSEC_VALIDATED;
+ t->answer_authenticated = true;
+ return 0;
+ }
+
+ /* Cached stuff is not affected by validation. */
+ if (t->answer_source != DNS_TRANSACTION_NETWORK)
+ return 0;
+
+ if (!dns_transaction_dnssec_supported_full(t)) {
+ /* The server does not support DNSSEC, or doesn't augment responses with RRSIGs. */
+ t->answer_dnssec_result = DNSSEC_INCOMPATIBLE_SERVER;
+ log_debug("Not validating response, server lacks DNSSEC support.");
+ return 0;
+ }
+
+ log_debug("Validating response from transaction %" PRIu16 " (%s).", t->id, dns_transaction_key_string(t));
+
+ /* First, see if this response contains any revoked trust
+ * anchors we care about */
+ r = dns_transaction_check_revoked_trust_anchors(t);
+ if (r < 0)
+ return r;
+
+ /* Third, copy all RRs we acquired successfully from auxiliary RRs over. */
+ r = dns_transaction_copy_validated(t);
+ if (r < 0)
+ return r;
+
+ /* Second, see if there are DNSKEYs we already know a
+ * validated DS for. */
+ r = dns_transaction_validate_dnskey_by_ds(t);
+ if (r < 0)
+ return r;
+
+ /* Fourth, remove all DNSKEY and DS RRs again that our trust
+ * anchor says are revoked. After all we might have marked
+ * some keys revoked above, but they might still be lingering
+ * in our validated_keys list. */
+ r = dns_transaction_invalidate_revoked_keys(t);
+ if (r < 0)
+ return r;
+
+ phase = PHASE_DNSKEY;
+ for (;;) {
+ bool changed = false, have_nsec = false;
+
+ DNS_ANSWER_FOREACH(rr, t->answer) {
+ DnsResourceRecord *rrsig = NULL;
+ DnssecResult result;
+
+ switch (rr->key->type) {
+
+ case DNS_TYPE_RRSIG:
+ continue;
+
+ case DNS_TYPE_DNSKEY:
+ /* We validate DNSKEYs only in the DNSKEY and ALL phases */
+ if (phase == PHASE_NSEC)
+ continue;
+ break;
+
+ case DNS_TYPE_NSEC:
+ case DNS_TYPE_NSEC3:
+ have_nsec = true;
+
+ /* We validate NSEC/NSEC3 only in the NSEC and ALL phases */
+ if (phase == PHASE_DNSKEY)
+ continue;
+
+ break;
+
+ default:
+ /* We validate all other RRs only in the ALL phases */
+ if (phase != PHASE_ALL)
+ continue;
+
+ break;
+ }
+
+ r = dnssec_verify_rrset_search(t->answer, rr->key, t->validated_keys, USEC_INFINITY, &result, &rrsig);
+ if (r < 0)
+ return r;
+
+ log_debug("Looking at %s: %s", strna(dns_resource_record_to_string(rr)), dnssec_result_to_string(result));
+
+ if (result == DNSSEC_VALIDATED) {
+
+ if (rr->key->type == DNS_TYPE_DNSKEY) {
+ /* If we just validated a
+ * DNSKEY RRset, then let's
+ * add these keys to the set
+ * of validated keys for this
+ * transaction. */
+
+ r = dns_answer_copy_by_key(&t->validated_keys, t->answer, rr->key, DNS_ANSWER_AUTHENTICATED);
+ if (r < 0)
+ return r;
+
+ /* some of the DNSKEYs we just
+ * added might already have
+ * been revoked, remove them
+ * again in that case. */
+ r = dns_transaction_invalidate_revoked_keys(t);
+ if (r < 0)
+ return r;
+ }
+
+ /* Add the validated RRset to the new
+ * list of validated RRsets, and
+ * remove it from the unvalidated
+ * RRsets. We mark the RRset as
+ * authenticated and cacheable. */
+ r = dns_answer_move_by_key(&validated, &t->answer, rr->key, DNS_ANSWER_AUTHENTICATED|DNS_ANSWER_CACHEABLE);
+ if (r < 0)
+ return r;
+
+ t->scope->manager->n_dnssec_secure++;
+
+ /* Exit the loop, we dropped something from the answer, start from the beginning */
+ changed = true;
+ break;
+ }
+
+ /* If we haven't read all DNSKEYs yet a negative result of the validation is irrelevant, as
+ * there might be more DNSKEYs coming. Similar, if we haven't read all NSEC/NSEC3 RRs yet, we
+ * cannot do positive wildcard proofs yet, as those require the NSEC/NSEC3 RRs. */
+ if (phase != PHASE_ALL)
+ continue;
+
+ if (result == DNSSEC_VALIDATED_WILDCARD) {
+ bool authenticated = false;
+ const char *source;
+
+ /* This RRset validated, but as a wildcard. This means we need to prove via NSEC/NSEC3
+ * that no matching non-wildcard RR exists.*/
+
+ /* First step, determine the source of synthesis */
+ r = dns_resource_record_source(rrsig, &source);
+ if (r < 0)
+ return r;
+
+ r = dnssec_test_positive_wildcard(
+ validated,
+ DNS_RESOURCE_KEY_NAME(rr->key),
+ source,
+ rrsig->rrsig.signer,
+ &authenticated);
+
+ /* Unless the NSEC proof showed that the key really doesn't exist something is off. */
+ if (r == 0)
+ result = DNSSEC_INVALID;
+ else {
+ r = dns_answer_move_by_key(&validated, &t->answer, rr->key, authenticated ? (DNS_ANSWER_AUTHENTICATED|DNS_ANSWER_CACHEABLE) : 0);
+ if (r < 0)
+ return r;
+
+ if (authenticated)
+ t->scope->manager->n_dnssec_secure++;
+ else
+ t->scope->manager->n_dnssec_insecure++;
+
+ /* Exit the loop, we dropped something from the answer, start from the beginning */
+ changed = true;
+ break;
+ }
+ }
+
+ if (result == DNSSEC_NO_SIGNATURE) {
+ r = dns_transaction_requires_rrsig(t, rr);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ /* Data does not require signing. In that case, just copy it over,
+ * but remember that this is by no means authenticated.*/
+ r = dns_answer_move_by_key(&validated, &t->answer, rr->key, 0);
+ if (r < 0)
+ return r;
+
+ t->scope->manager->n_dnssec_insecure++;
+ changed = true;
+ break;
+ }
+
+ r = dns_transaction_known_signed(t, rr);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ /* This is an RR we know has to be signed. If it isn't this means
+ * the server is not attaching RRSIGs, hence complain. */
+
+ dns_server_packet_rrsig_missing(t->server, t->current_feature_level);
+
+ if (t->scope->dnssec_mode == DNSSEC_ALLOW_DOWNGRADE) {
+
+ /* Downgrading is OK? If so, just consider the information unsigned */
+
+ r = dns_answer_move_by_key(&validated, &t->answer, rr->key, 0);
+ if (r < 0)
+ return r;
+
+ t->scope->manager->n_dnssec_insecure++;
+ changed = true;
+ break;
+ }
+
+ /* Otherwise, fail */
+ t->answer_dnssec_result = DNSSEC_INCOMPATIBLE_SERVER;
+ return 0;
+ }
+
+ r = dns_transaction_in_private_tld(t, rr->key);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ _cleanup_free_ char *s = NULL;
+
+ /* The data is from a TLD that is proven not to exist, and we are in downgrade
+ * mode, hence ignore the fact that this was not signed. */
+
+ (void) dns_resource_key_to_string(rr->key, &s);
+ log_info("Detected RRset %s is in a private DNS zone, permitting unsigned RRs.", strna(s ? strstrip(s) : NULL));
+
+ r = dns_answer_move_by_key(&validated, &t->answer, rr->key, 0);
+ if (r < 0)
+ return r;
+
+ t->scope->manager->n_dnssec_insecure++;
+ changed = true;
+ break;
+ }
+ }
+
+ if (IN_SET(result,
+ DNSSEC_MISSING_KEY,
+ DNSSEC_SIGNATURE_EXPIRED,
+ DNSSEC_UNSUPPORTED_ALGORITHM)) {
+
+ r = dns_transaction_dnskey_authenticated(t, rr);
+ if (r < 0 && r != -ENXIO)
+ return r;
+ if (r == 0) {
+ /* The DNSKEY transaction was not authenticated, this means there's
+ * no DS for this, which means it's OK if no keys are found for this signature. */
+
+ r = dns_answer_move_by_key(&validated, &t->answer, rr->key, 0);
+ if (r < 0)
+ return r;
+
+ t->scope->manager->n_dnssec_insecure++;
+ changed = true;
+ break;
+ }
+ }
+
+ if (IN_SET(result,
+ DNSSEC_INVALID,
+ DNSSEC_SIGNATURE_EXPIRED,
+ DNSSEC_NO_SIGNATURE))
+ t->scope->manager->n_dnssec_bogus++;
+ else /* DNSSEC_MISSING_KEY or DNSSEC_UNSUPPORTED_ALGORITHM */
+ t->scope->manager->n_dnssec_indeterminate++;
+
+ r = dns_transaction_is_primary_response(t, rr);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+
+ /* Look for a matching DNAME for this CNAME */
+ r = dns_answer_has_dname_for_cname(t->answer, rr);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ /* Also look among the stuff we already validated */
+ r = dns_answer_has_dname_for_cname(validated, rr);
+ if (r < 0)
+ return r;
+ }
+
+ if (r == 0) {
+ /* This is a primary response to our question, and it failed validation. That's
+ * fatal. */
+ t->answer_dnssec_result = result;
+ return 0;
+ }
+
+ /* This is a primary response, but we do have a DNAME RR in the RR that can replay this
+ * CNAME, hence rely on that, and we can remove the CNAME in favour of it. */
+ }
+
+ /* This is just some auxiliary data. Just remove the RRset and continue. */
+ r = dns_answer_remove_by_key(&t->answer, rr->key);
+ if (r < 0)
+ return r;
+
+ /* Exit the loop, we dropped something from the answer, start from the beginning */
+ changed = true;
+ break;
+ }
+
+ /* Restart the inner loop as long as we managed to achieve something */
+ if (changed)
+ continue;
+
+ if (phase == PHASE_DNSKEY && have_nsec) {
+ /* OK, we processed all DNSKEYs, and there are NSEC/NSEC3 RRs, look at those now. */
+ phase = PHASE_NSEC;
+ continue;
+ }
+
+ if (phase != PHASE_ALL) {
+ /* OK, we processed all DNSKEYs and NSEC/NSEC3 RRs, look at all the rest now. Note that in this
+ * third phase we start to remove RRs we couldn't validate. */
+ phase = PHASE_ALL;
+ continue;
+ }
+
+ /* We're done */
+ break;
+ }
+
+ dns_answer_unref(t->answer);
+ t->answer = validated;
+ validated = NULL;
+
+ /* At this point the answer only contains validated
+ * RRsets. Now, let's see if it actually answers the question
+ * we asked. If so, great! If it doesn't, then see if
+ * NSEC/NSEC3 can prove this. */
+ r = dns_transaction_has_positive_answer(t, &flags);
+ if (r > 0) {
+ /* Yes, it answers the question! */
+
+ if (flags & DNS_ANSWER_AUTHENTICATED) {
+ /* The answer is fully authenticated, yay. */
+ t->answer_dnssec_result = DNSSEC_VALIDATED;
+ t->answer_rcode = DNS_RCODE_SUCCESS;
+ t->answer_authenticated = true;
+ } else {
+ /* The answer is not fully authenticated. */
+ t->answer_dnssec_result = DNSSEC_UNSIGNED;
+ t->answer_authenticated = false;
+ }
+
+ } else if (r == 0) {
+ DnssecNsecResult nr;
+ bool authenticated = false;
+
+ /* Bummer! Let's check NSEC/NSEC3 */
+ r = dnssec_nsec_test(t->answer, t->key, &nr, &authenticated, &t->answer_nsec_ttl);
+ if (r < 0)
+ return r;
+
+ switch (nr) {
+
+ case DNSSEC_NSEC_NXDOMAIN:
+ /* NSEC proves the domain doesn't exist. Very good. */
+ log_debug("Proved NXDOMAIN via NSEC/NSEC3 for transaction %u (%s)", t->id, dns_transaction_key_string(t));
+ t->answer_dnssec_result = DNSSEC_VALIDATED;
+ t->answer_rcode = DNS_RCODE_NXDOMAIN;
+ t->answer_authenticated = authenticated;
+ break;
+
+ case DNSSEC_NSEC_NODATA:
+ /* NSEC proves that there's no data here, very good. */
+ log_debug("Proved NODATA via NSEC/NSEC3 for transaction %u (%s)", t->id, dns_transaction_key_string(t));
+ t->answer_dnssec_result = DNSSEC_VALIDATED;
+ t->answer_rcode = DNS_RCODE_SUCCESS;
+ t->answer_authenticated = authenticated;
+ break;
+
+ case DNSSEC_NSEC_OPTOUT:
+ /* NSEC3 says the data might not be signed */
+ log_debug("Data is NSEC3 opt-out via NSEC/NSEC3 for transaction %u (%s)", t->id, dns_transaction_key_string(t));
+ t->answer_dnssec_result = DNSSEC_UNSIGNED;
+ t->answer_authenticated = false;
+ break;
+
+ case DNSSEC_NSEC_NO_RR:
+ /* No NSEC data? Bummer! */
+
+ r = dns_transaction_requires_nsec(t);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ t->answer_dnssec_result = DNSSEC_NO_SIGNATURE;
+ else {
+ t->answer_dnssec_result = DNSSEC_UNSIGNED;
+ t->answer_authenticated = false;
+ }
+
+ break;
+
+ case DNSSEC_NSEC_UNSUPPORTED_ALGORITHM:
+ /* We don't know the NSEC3 algorithm used? */
+ t->answer_dnssec_result = DNSSEC_UNSUPPORTED_ALGORITHM;
+ break;
+
+ case DNSSEC_NSEC_FOUND:
+ case DNSSEC_NSEC_CNAME:
+ /* NSEC says it needs to be there, but we couldn't find it? Bummer! */
+ t->answer_dnssec_result = DNSSEC_NSEC_MISMATCH;
+ break;
+
+ default:
+ assert_not_reached("Unexpected NSEC result.");
+ }
+ }
+
+ return 1;
+}
+
+const char *dns_transaction_key_string(DnsTransaction *t) {
+ assert(t);
+
+ if (!t->key_string) {
+ if (dns_resource_key_to_string(t->key, &t->key_string) < 0)
+ return "n/a";
+ }
+
+ return strstrip(t->key_string);
+}
+
static const char* const dns_transaction_state_table[_DNS_TRANSACTION_STATE_MAX] = {
[DNS_TRANSACTION_NULL] = "null",
[DNS_TRANSACTION_PENDING] = "pending",
- [DNS_TRANSACTION_FAILURE] = "failure",
+ [DNS_TRANSACTION_VALIDATING] = "validating",
+ [DNS_TRANSACTION_RCODE_FAILURE] = "rcode-failure",
[DNS_TRANSACTION_SUCCESS] = "success",
[DNS_TRANSACTION_NO_SERVERS] = "no-servers",
[DNS_TRANSACTION_TIMEOUT] = "timeout",
@@ -741,5 +2966,16 @@ static const char* const dns_transaction_state_table[_DNS_TRANSACTION_STATE_MAX]
[DNS_TRANSACTION_INVALID_REPLY] = "invalid-reply",
[DNS_TRANSACTION_RESOURCES] = "resources",
[DNS_TRANSACTION_ABORTED] = "aborted",
+ [DNS_TRANSACTION_DNSSEC_FAILED] = "dnssec-failed",
+ [DNS_TRANSACTION_NO_TRUST_ANCHOR] = "no-trust-anchor",
+ [DNS_TRANSACTION_RR_TYPE_UNSUPPORTED] = "rr-type-unsupported",
};
DEFINE_STRING_TABLE_LOOKUP(dns_transaction_state, DnsTransactionState);
+
+static const char* const dns_transaction_source_table[_DNS_TRANSACTION_SOURCE_MAX] = {
+ [DNS_TRANSACTION_NETWORK] = "network",
+ [DNS_TRANSACTION_CACHE] = "cache",
+ [DNS_TRANSACTION_ZONE] = "zone",
+ [DNS_TRANSACTION_TRUST_ANCHOR] = "trust-anchor",
+};
+DEFINE_STRING_TABLE_LOOKUP(dns_transaction_source, DnsTransactionSource);
diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h
index acf6a6f651..76cf6e71db 100644
--- a/src/resolve/resolved-dns-transaction.h
+++ b/src/resolve/resolved-dns-transaction.h
@@ -23,11 +23,13 @@
typedef struct DnsTransaction DnsTransaction;
typedef enum DnsTransactionState DnsTransactionState;
+typedef enum DnsTransactionSource DnsTransactionSource;
enum DnsTransactionState {
DNS_TRANSACTION_NULL,
DNS_TRANSACTION_PENDING,
- DNS_TRANSACTION_FAILURE,
+ DNS_TRANSACTION_VALIDATING,
+ DNS_TRANSACTION_RCODE_FAILURE,
DNS_TRANSACTION_SUCCESS,
DNS_TRANSACTION_NO_SERVERS,
DNS_TRANSACTION_TIMEOUT,
@@ -35,49 +37,102 @@ enum DnsTransactionState {
DNS_TRANSACTION_INVALID_REPLY,
DNS_TRANSACTION_RESOURCES,
DNS_TRANSACTION_ABORTED,
+ DNS_TRANSACTION_DNSSEC_FAILED,
+ DNS_TRANSACTION_NO_TRUST_ANCHOR,
+ DNS_TRANSACTION_RR_TYPE_UNSUPPORTED,
_DNS_TRANSACTION_STATE_MAX,
_DNS_TRANSACTION_STATE_INVALID = -1
};
-#include "resolved-dns-scope.h"
+#define DNS_TRANSACTION_IS_LIVE(state) IN_SET((state), DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_VALIDATING)
+
+enum DnsTransactionSource {
+ DNS_TRANSACTION_NETWORK,
+ DNS_TRANSACTION_CACHE,
+ DNS_TRANSACTION_ZONE,
+ DNS_TRANSACTION_TRUST_ANCHOR,
+ _DNS_TRANSACTION_SOURCE_MAX,
+ _DNS_TRANSACTION_SOURCE_INVALID = -1
+};
+
+#include "resolved-dns-answer.h"
#include "resolved-dns-packet.h"
#include "resolved-dns-question.h"
-#include "resolved-dns-answer.h"
+#include "resolved-dns-scope.h"
struct DnsTransaction {
DnsScope *scope;
DnsResourceKey *key;
+ char *key_string;
DnsTransactionState state;
+
uint16_t id;
- bool initial_jitter;
+ bool tried_stream:1;
+
+ bool initial_jitter_scheduled:1;
+ bool initial_jitter_elapsed:1;
DnsPacket *sent, *received;
- DnsAnswer *cached;
- int cached_rcode;
+
+ DnsAnswer *answer;
+ int answer_rcode;
+ DnssecResult answer_dnssec_result;
+ DnsTransactionSource answer_source;
+ uint32_t answer_nsec_ttl;
+
+ /* Indicates whether the primary answer is authenticated,
+ * i.e. whether the RRs from answer which directly match the
+ * question are authenticated, or, if there are none, whether
+ * the NODATA or NXDOMAIN case is. It says nothing about
+ * additional RRs listed in the answer, however they have
+ * their own DNS_ANSWER_AUTHORIZED FLAGS. Note that this bit
+ * is defined different than the AD bit in DNS packets, as
+ * that covers more than just the actual primary answer. */
+ bool answer_authenticated;
+
+ /* Contains DNSKEY, DS, SOA RRs we already verified and need
+ * to authenticate this reply */
+ DnsAnswer *validated_keys;
usec_t start_usec;
+ usec_t next_attempt_after;
sd_event_source *timeout_event_source;
unsigned n_attempts;
+ /* UDP connection logic, if we need it */
int dns_udp_fd;
sd_event_source *dns_udp_event_source;
+ /* TCP connection logic, if we need it */
+ DnsStream *stream;
+
/* The active server */
DnsServer *server;
- /* TCP connection logic, if we need it */
- DnsStream *stream;
+ /* The features of the DNS server at time of transaction start */
+ DnsServerFeatureLevel current_feature_level;
- /* Queries this transaction is referenced by and that shall be
- * notified about this specific transaction completing. */
- Set *queries;
+ /* Query candidates this transaction is referenced by and that
+ * shall be notified about this specific transaction
+ * completing. */
+ Set *notify_query_candidates;
/* Zone items this transaction is referenced by and that shall
* be notified about completion. */
- Set *zone_items;
+ Set *notify_zone_items;
+
+ /* Other transactions that this transactions is referenced by
+ * and that shall be notified about completion. This is used
+ * when transactions want to validate their RRsets, but need
+ * another DNSKEY or DS RR to do so. */
+ Set *notify_transactions;
+
+ /* The opposite direction: the transactions this transaction
+ * created in order to request DNSKEY or DS RRs. */
+ Set *dnssec_transactions;
unsigned block_gc;
@@ -87,22 +142,35 @@ struct DnsTransaction {
int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key);
DnsTransaction* dns_transaction_free(DnsTransaction *t);
-void dns_transaction_gc(DnsTransaction *t);
+bool dns_transaction_gc(DnsTransaction *t);
int dns_transaction_go(DnsTransaction *t);
void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p);
void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state);
+void dns_transaction_notify(DnsTransaction *t, DnsTransaction *source);
+int dns_transaction_validate_dnssec(DnsTransaction *t);
+int dns_transaction_request_dnssec_keys(DnsTransaction *t);
+
+const char *dns_transaction_key_string(DnsTransaction *t);
+
const char* dns_transaction_state_to_string(DnsTransactionState p) _const_;
DnsTransactionState dns_transaction_state_from_string(const char *s) _pure_;
+const char* dns_transaction_source_to_string(DnsTransactionSource p) _const_;
+DnsTransactionSource dns_transaction_source_from_string(const char *s) _pure_;
+
/* LLMNR Jitter interval, see RFC 4795 Section 7 */
#define LLMNR_JITTER_INTERVAL_USEC (100 * USEC_PER_MSEC)
+/* mDNS Jitter interval, see RFC 6762 Section 5.2 */
+#define MDNS_JITTER_MIN_USEC (20 * USEC_PER_MSEC)
+#define MDNS_JITTER_RANGE_USEC (100 * USEC_PER_MSEC)
+
/* Maximum attempts to send DNS requests, across all DNS servers */
#define DNS_TRANSACTION_ATTEMPTS_MAX 16
/* Maximum attempts to send LLMNR requests, see RFC 4795 Section 2.7 */
#define LLMNR_TRANSACTION_ATTEMPTS_MAX 3
-#define TRANSACTION_ATTEMPTS_MAX(p) (p == DNS_PROTOCOL_LLMNR ? LLMNR_TRANSACTION_ATTEMPTS_MAX : DNS_TRANSACTION_ATTEMPTS_MAX)
+#define TRANSACTION_ATTEMPTS_MAX(p) ((p) == DNS_PROTOCOL_LLMNR ? LLMNR_TRANSACTION_ATTEMPTS_MAX : DNS_TRANSACTION_ATTEMPTS_MAX)
diff --git a/src/resolve/resolved-dns-trust-anchor.c b/src/resolve/resolved-dns-trust-anchor.c
new file mode 100644
index 0000000000..02d7ac91e1
--- /dev/null
+++ b/src/resolve/resolved-dns-trust-anchor.c
@@ -0,0 +1,745 @@
+/*-*- 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 <sd-messages.h>
+
+#include "alloc-util.h"
+#include "conf-files.h"
+#include "def.h"
+#include "dns-domain.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "hexdecoct.h"
+#include "parse-util.h"
+#include "resolved-dns-trust-anchor.h"
+#include "resolved-dns-dnssec.h"
+#include "set.h"
+#include "string-util.h"
+#include "strv.h"
+
+static const char trust_anchor_dirs[] = CONF_PATHS_NULSTR("dnssec-trust-anchors.d");
+
+/* The DS RR from https://data.iana.org/root-anchors/root-anchors.xml, retrieved December 2015 */
+static const uint8_t root_digest[] =
+ { 0x49, 0xAA, 0xC1, 0x1D, 0x7B, 0x6F, 0x64, 0x46, 0x70, 0x2E, 0x54, 0xA1, 0x60, 0x73, 0x71, 0x60,
+ 0x7A, 0x1A, 0x41, 0x85, 0x52, 0x00, 0xFD, 0x2C, 0xE1, 0xCD, 0xDE, 0x32, 0xF2, 0x4E, 0x8F, 0xB5 };
+
+static bool dns_trust_anchor_knows_domain_positive(DnsTrustAnchor *d, const char *name) {
+ assert(d);
+
+ /* Returns true if there's an entry for the specified domain
+ * name in our trust anchor */
+
+ return
+ hashmap_contains(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DNSKEY, name)) ||
+ hashmap_contains(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DS, name));
+}
+
+static int dns_trust_anchor_add_builtin_positive(DnsTrustAnchor *d) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+ _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
+ int r;
+
+ assert(d);
+
+ r = hashmap_ensure_allocated(&d->positive_by_key, &dns_resource_key_hash_ops);
+ if (r < 0)
+ return r;
+
+ /* Only add the built-in trust anchor if there's neither a DS
+ * nor a DNSKEY defined for the root domain. That way users
+ * have an easy way to override the root domain DS/DNSKEY
+ * data. */
+ if (dns_trust_anchor_knows_domain_positive(d, "."))
+ return 0;
+
+ /* Add the RR from https://data.iana.org/root-anchors/root-anchors.xml */
+ rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, "");
+ if (!rr)
+ return -ENOMEM;
+
+ rr->ds.key_tag = 19036;
+ rr->ds.algorithm = DNSSEC_ALGORITHM_RSASHA256;
+ rr->ds.digest_type = DNSSEC_DIGEST_SHA256;
+ rr->ds.digest_size = sizeof(root_digest);
+ rr->ds.digest = memdup(root_digest, rr->ds.digest_size);
+ if (!rr->ds.digest)
+ return -ENOMEM;
+
+ answer = dns_answer_new(1);
+ if (!answer)
+ return -ENOMEM;
+
+ r = dns_answer_add(answer, rr, 0, DNS_ANSWER_AUTHENTICATED);
+ if (r < 0)
+ return r;
+
+ r = hashmap_put(d->positive_by_key, rr->key, answer);
+ if (r < 0)
+ return r;
+
+ answer = NULL;
+ return 0;
+}
+
+static int dns_trust_anchor_add_builtin_negative(DnsTrustAnchor *d) {
+
+ static const char private_domains[] =
+ /* RFC 6761 says that .test is a special domain for
+ * testing and not to be installed in the root zone */
+ "test\0"
+
+ /* RFC 6761 says that these reverse IP lookup ranges
+ * are for private addresses, and hence should not
+ * show up in the root zone */
+ "10.in-addr.arpa\0"
+ "16.172.in-addr.arpa\0"
+ "17.172.in-addr.arpa\0"
+ "18.172.in-addr.arpa\0"
+ "19.172.in-addr.arpa\0"
+ "20.172.in-addr.arpa\0"
+ "21.172.in-addr.arpa\0"
+ "22.172.in-addr.arpa\0"
+ "23.172.in-addr.arpa\0"
+ "24.172.in-addr.arpa\0"
+ "25.172.in-addr.arpa\0"
+ "26.172.in-addr.arpa\0"
+ "27.172.in-addr.arpa\0"
+ "28.172.in-addr.arpa\0"
+ "29.172.in-addr.arpa\0"
+ "30.172.in-addr.arpa\0"
+ "31.172.in-addr.arpa\0"
+ "168.192.in-addr.arpa\0"
+
+ /* RFC 6762 reserves the .local domain for Multicast
+ * DNS, it hence cannot appear in the root zone. (Note
+ * that we by default do not route .local traffic to
+ * DNS anyway, except when a configured search domain
+ * suggests so.) */
+ "local\0"
+
+ /* These two are well known, popular private zone
+ * TLDs, that are blocked from delegation, according
+ * to:
+ * http://icannwiki.com/Name_Collision#NGPC_Resolution
+ *
+ * There's also ongoing work on making this official
+ * in an RRC:
+ * https://www.ietf.org/archive/id/draft-chapin-additional-reserved-tlds-02.txt */
+ "home\0"
+ "corp\0"
+
+ /* The following four TLDs are suggested for private
+ * zones in RFC 6762, Appendix G, and are hence very
+ * unlikely to be made official TLDs any day soon */
+ "lan\0"
+ "intranet\0"
+ "internal\0"
+ "private\0";
+
+ const char *name;
+ int r;
+
+ assert(d);
+
+ /* Only add the built-in trust anchor if there's no negative
+ * trust anchor defined at all. This enables easy overriding
+ * of negative trust anchors. */
+
+ if (set_size(d->negative_by_name) > 0)
+ return 0;
+
+ r = set_ensure_allocated(&d->negative_by_name, &dns_name_hash_ops);
+ if (r < 0)
+ return r;
+
+ /* We add a couple of domains as default negative trust
+ * anchors, where it's very unlikely they will be installed in
+ * the root zone. If they exist they must be private, and thus
+ * unsigned. */
+
+ NULSTR_FOREACH(name, private_domains) {
+
+ if (dns_trust_anchor_knows_domain_positive(d, name))
+ continue;
+
+ r = set_put_strdup(d->negative_by_name, name);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int dns_trust_anchor_load_positive(DnsTrustAnchor *d, const char *path, unsigned line, const char *s) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+ _cleanup_free_ char *domain = NULL, *class = NULL, *type = NULL;
+ _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
+ DnsAnswer *old_answer = NULL;
+ const char *p = s;
+ int r;
+
+ assert(d);
+ assert(line);
+
+ r = extract_first_word(&p, &domain, NULL, EXTRACT_QUOTES);
+ if (r < 0)
+ return log_warning_errno(r, "Unable to parse domain in line %s:%u: %m", path, line);
+
+ if (!dns_name_is_valid(domain)) {
+ log_warning("Domain name %s is invalid, at line %s:%u, ignoring line.", domain, path, line);
+ return -EINVAL;
+ }
+
+ r = extract_many_words(&p, NULL, 0, &class, &type, NULL);
+ if (r < 0)
+ return log_warning_errno(r, "Unable to parse class and type in line %s:%u: %m", path, line);
+ if (r != 2) {
+ log_warning("Missing class or type in line %s:%u", path, line);
+ return -EINVAL;
+ }
+
+ if (!strcaseeq(class, "IN")) {
+ log_warning("RR class %s is not supported, ignoring line %s:%u.", class, path, line);
+ return -EINVAL;
+ }
+
+ if (strcaseeq(type, "DS")) {
+ _cleanup_free_ char *key_tag = NULL, *algorithm = NULL, *digest_type = NULL, *digest = NULL;
+ _cleanup_free_ void *dd = NULL;
+ uint16_t kt;
+ int a, dt;
+ size_t l;
+
+ r = extract_many_words(&p, NULL, 0, &key_tag, &algorithm, &digest_type, &digest, NULL);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to parse DS parameters on line %s:%u: %m", path, line);
+ return -EINVAL;
+ }
+ if (r != 4) {
+ log_warning("Missing DS parameters on line %s:%u", path, line);
+ return -EINVAL;
+ }
+
+ r = safe_atou16(key_tag, &kt);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to parse DS key tag %s on line %s:%u: %m", key_tag, path, line);
+
+ a = dnssec_algorithm_from_string(algorithm);
+ if (a < 0) {
+ log_warning("Failed to parse DS algorithm %s on line %s:%u", algorithm, path, line);
+ return -EINVAL;
+ }
+
+ dt = dnssec_digest_from_string(digest_type);
+ if (dt < 0) {
+ log_warning("Failed to parse DS digest type %s on line %s:%u", digest_type, path, line);
+ return -EINVAL;
+ }
+
+ r = unhexmem(digest, strlen(digest), &dd, &l);
+ if (r < 0) {
+ log_warning("Failed to parse DS digest %s on line %s:%u", digest, path, line);
+ return -EINVAL;
+ }
+
+ rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, domain);
+ if (!rr)
+ return log_oom();
+
+ rr->ds.key_tag = kt;
+ rr->ds.algorithm = a;
+ rr->ds.digest_type = dt;
+ rr->ds.digest_size = l;
+ rr->ds.digest = dd;
+ dd = NULL;
+
+ } else if (strcaseeq(type, "DNSKEY")) {
+ _cleanup_free_ char *flags = NULL, *protocol = NULL, *algorithm = NULL, *key = NULL;
+ _cleanup_free_ void *k = NULL;
+ uint16_t f;
+ size_t l;
+ int a;
+
+ r = extract_many_words(&p, NULL, 0, &flags, &protocol, &algorithm, &key, NULL);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to parse DNSKEY parameters on line %s:%u: %m", path, line);
+ if (r != 4) {
+ log_warning("Missing DNSKEY parameters on line %s:%u", path, line);
+ return -EINVAL;
+ }
+
+ if (!streq(protocol, "3")) {
+ log_warning("DNSKEY Protocol is not 3 on line %s:%u", path, line);
+ return -EINVAL;
+ }
+
+ r = safe_atou16(flags, &f);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to parse DNSKEY flags field %s on line %s:%u", flags, path, line);
+ if ((f & DNSKEY_FLAG_ZONE_KEY) == 0) {
+ log_warning("DNSKEY lacks zone key bit set on line %s:%u", path, line);
+ return -EINVAL;
+ }
+ if ((f & DNSKEY_FLAG_REVOKE)) {
+ log_warning("DNSKEY is already revoked on line %s:%u", path, line);
+ return -EINVAL;
+ }
+
+ a = dnssec_algorithm_from_string(algorithm);
+ if (a < 0) {
+ log_warning("Failed to parse DNSKEY algorithm %s on line %s:%u", algorithm, path, line);
+ return -EINVAL;
+ }
+
+ r = unbase64mem(key, strlen(key), &k, &l);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to parse DNSKEY key data %s on line %s:%u", key, path, line);
+
+ rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, domain);
+ if (!rr)
+ return log_oom();
+
+ rr->dnskey.flags = f;
+ rr->dnskey.protocol = 3;
+ rr->dnskey.algorithm = a;
+ rr->dnskey.key_size = l;
+ rr->dnskey.key = k;
+ k = NULL;
+
+ } else {
+ log_warning("RR type %s is not supported, ignoring line %s:%u.", type, path, line);
+ return -EINVAL;
+ }
+
+ if (!isempty(p)) {
+ log_warning("Trailing garbage on line %s:%u, ignoring line.", path, line);
+ return -EINVAL;
+ }
+
+ r = hashmap_ensure_allocated(&d->positive_by_key, &dns_resource_key_hash_ops);
+ if (r < 0)
+ return log_oom();
+
+ old_answer = hashmap_get(d->positive_by_key, rr->key);
+ answer = dns_answer_ref(old_answer);
+
+ r = dns_answer_add_extend(&answer, rr, 0, DNS_ANSWER_AUTHENTICATED);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add trust anchor RR: %m");
+
+ r = hashmap_replace(d->positive_by_key, rr->key, answer);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add answer to trust anchor: %m");
+
+ old_answer = dns_answer_unref(old_answer);
+ answer = NULL;
+
+ return 0;
+}
+
+static int dns_trust_anchor_load_negative(DnsTrustAnchor *d, const char *path, unsigned line, const char *s) {
+ _cleanup_free_ char *domain = NULL;
+ const char *p = s;
+ int r;
+
+ assert(d);
+ assert(line);
+
+ r = extract_first_word(&p, &domain, NULL, EXTRACT_QUOTES);
+ if (r < 0)
+ return log_warning_errno(r, "Unable to parse line %s:%u: %m", path, line);
+
+ if (!dns_name_is_valid(domain)) {
+ log_warning("Domain name %s is invalid, at line %s:%u, ignoring line.", domain, path, line);
+ return -EINVAL;
+ }
+
+ if (!isempty(p)) {
+ log_warning("Trailing garbage at line %s:%u, ignoring line.", path, line);
+ return -EINVAL;
+ }
+
+ r = set_ensure_allocated(&d->negative_by_name, &dns_name_hash_ops);
+ if (r < 0)
+ return log_oom();
+
+ r = set_put(d->negative_by_name, domain);
+ if (r < 0)
+ return log_oom();
+ if (r > 0)
+ domain = NULL;
+
+ return 0;
+}
+
+static int dns_trust_anchor_load_files(
+ DnsTrustAnchor *d,
+ const char *suffix,
+ int (*loader)(DnsTrustAnchor *d, const char *path, unsigned n, const char *line)) {
+
+ _cleanup_strv_free_ char **files = NULL;
+ char **f;
+ int r;
+
+ assert(d);
+ assert(suffix);
+ assert(loader);
+
+ r = conf_files_list_nulstr(&files, suffix, NULL, trust_anchor_dirs);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enumerate %s trust anchor files: %m", suffix);
+
+ STRV_FOREACH(f, files) {
+ _cleanup_fclose_ FILE *g = NULL;
+ char line[LINE_MAX];
+ unsigned n = 0;
+
+ g = fopen(*f, "r");
+ if (!g) {
+ if (errno == ENOENT)
+ continue;
+
+ log_warning_errno(errno, "Failed to open %s: %m", *f);
+ continue;
+ }
+
+ FOREACH_LINE(line, g, log_warning_errno(errno, "Failed to read %s, ignoring: %m", *f)) {
+ char *l;
+
+ n++;
+
+ l = strstrip(line);
+ if (isempty(l))
+ continue;
+
+ if (*l == ';')
+ continue;
+
+ (void) loader(d, *f, n, l);
+ }
+ }
+
+ return 0;
+}
+
+static int domain_name_cmp(const void *a, const void *b) {
+ char **x = (char**) a, **y = (char**) b;
+
+ return dns_name_compare_func(*x, *y);
+}
+
+static int dns_trust_anchor_dump(DnsTrustAnchor *d) {
+ DnsAnswer *a;
+ Iterator i;
+
+ assert(d);
+
+ if (hashmap_isempty(d->positive_by_key))
+ log_info("No positive trust anchors defined.");
+ else {
+ log_info("Positive Trust Anchors:");
+ HASHMAP_FOREACH(a, d->positive_by_key, i) {
+ DnsResourceRecord *rr;
+
+ DNS_ANSWER_FOREACH(rr, a)
+ log_info("%s", dns_resource_record_to_string(rr));
+ }
+ }
+
+ if (set_isempty(d->negative_by_name))
+ log_info("No negative trust anchors defined.");
+ else {
+ _cleanup_free_ char **l = NULL, *j = NULL;
+
+ l = set_get_strv(d->negative_by_name);
+ if (!l)
+ return log_oom();
+
+ qsort_safe(l, set_size(d->negative_by_name), sizeof(char*), domain_name_cmp);
+
+ j = strv_join(l, " ");
+ if (!j)
+ return log_oom();
+
+ log_info("Negative trust anchors: %s", j);
+ }
+
+ return 0;
+}
+
+int dns_trust_anchor_load(DnsTrustAnchor *d) {
+ int r;
+
+ assert(d);
+
+ /* If loading things from disk fails, we don't consider this fatal */
+ (void) dns_trust_anchor_load_files(d, ".positive", dns_trust_anchor_load_positive);
+ (void) dns_trust_anchor_load_files(d, ".negative", dns_trust_anchor_load_negative);
+
+ /* However, if the built-in DS fails, then we have a problem. */
+ r = dns_trust_anchor_add_builtin_positive(d);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add built-in positive trust anchor: %m");
+
+ r = dns_trust_anchor_add_builtin_negative(d);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add built-in negative trust anchor: %m");
+
+ dns_trust_anchor_dump(d);
+
+ return 0;
+}
+
+void dns_trust_anchor_flush(DnsTrustAnchor *d) {
+ DnsAnswer *a;
+ DnsResourceRecord *rr;
+
+ assert(d);
+
+ while ((a = hashmap_steal_first(d->positive_by_key)))
+ dns_answer_unref(a);
+ d->positive_by_key = hashmap_free(d->positive_by_key);
+
+ while ((rr = set_steal_first(d->revoked_by_rr)))
+ dns_resource_record_unref(rr);
+ d->revoked_by_rr = set_free(d->revoked_by_rr);
+
+ d->negative_by_name = set_free_free(d->negative_by_name);
+}
+
+int dns_trust_anchor_lookup_positive(DnsTrustAnchor *d, const DnsResourceKey *key, DnsAnswer **ret) {
+ DnsAnswer *a;
+
+ assert(d);
+ assert(key);
+ assert(ret);
+
+ /* We only serve DS and DNSKEY RRs. */
+ if (!IN_SET(key->type, DNS_TYPE_DS, DNS_TYPE_DNSKEY))
+ return 0;
+
+ a = hashmap_get(d->positive_by_key, key);
+ if (!a)
+ return 0;
+
+ *ret = dns_answer_ref(a);
+ return 1;
+}
+
+int dns_trust_anchor_lookup_negative(DnsTrustAnchor *d, const char *name) {
+ assert(d);
+ assert(name);
+
+ return set_contains(d->negative_by_name, name);
+}
+
+static int dns_trust_anchor_revoked_put(DnsTrustAnchor *d, DnsResourceRecord *rr) {
+ int r;
+
+ assert(d);
+
+ r = set_ensure_allocated(&d->revoked_by_rr, &dns_resource_record_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = set_put(d->revoked_by_rr, rr);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ dns_resource_record_ref(rr);
+
+ return r;
+}
+
+static int dns_trust_anchor_remove_revoked(DnsTrustAnchor *d, DnsResourceRecord *rr) {
+ _cleanup_(dns_answer_unrefp) DnsAnswer *new_answer = NULL;
+ DnsAnswer *old_answer;
+ int r;
+
+ /* Remember that this is a revoked trust anchor RR */
+ r = dns_trust_anchor_revoked_put(d, rr);
+ if (r < 0)
+ return r;
+
+ /* Remove this from the positive trust anchor */
+ old_answer = hashmap_get(d->positive_by_key, rr->key);
+ if (!old_answer)
+ return 0;
+
+ new_answer = dns_answer_ref(old_answer);
+
+ r = dns_answer_remove_by_rr(&new_answer, rr);
+ if (r <= 0)
+ return r;
+
+ /* We found the key! Warn the user */
+ log_struct(LOG_WARNING,
+ LOG_MESSAGE_ID(SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED),
+ LOG_MESSAGE("DNSSEC Trust anchor %s has been revoked. Please update the trust anchor, or upgrade your operating system."), strna(dns_resource_record_to_string(rr)),
+ "TRUST_ANCHOR=%s", dns_resource_record_to_string(rr),
+ NULL);
+
+ if (dns_answer_size(new_answer) <= 0) {
+ assert_se(hashmap_remove(d->positive_by_key, rr->key) == old_answer);
+ dns_answer_unref(old_answer);
+ return 1;
+ }
+
+ r = hashmap_replace(d->positive_by_key, new_answer->items[0].rr->key, new_answer);
+ if (r < 0)
+ return r;
+
+ new_answer = NULL;
+ dns_answer_unref(old_answer);
+ return 1;
+}
+
+static int dns_trust_anchor_check_revoked_one(DnsTrustAnchor *d, DnsResourceRecord *revoked_dnskey) {
+ DnsAnswer *a;
+ int r;
+
+ assert(d);
+ assert(revoked_dnskey);
+ assert(revoked_dnskey->key->type == DNS_TYPE_DNSKEY);
+ assert(revoked_dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE);
+
+ a = hashmap_get(d->positive_by_key, revoked_dnskey->key);
+ if (a) {
+ DnsResourceRecord *anchor;
+
+ /* First, look for the precise DNSKEY in our trust anchor database */
+
+ DNS_ANSWER_FOREACH(anchor, a) {
+
+ if (anchor->dnskey.protocol != revoked_dnskey->dnskey.protocol)
+ continue;
+
+ if (anchor->dnskey.algorithm != revoked_dnskey->dnskey.algorithm)
+ continue;
+
+ if (anchor->dnskey.key_size != revoked_dnskey->dnskey.key_size)
+ continue;
+
+ /* Note that we allow the REVOKE bit to be
+ * different! It will be set in the revoked
+ * key, but unset in our version of it */
+ if (((anchor->dnskey.flags ^ revoked_dnskey->dnskey.flags) | DNSKEY_FLAG_REVOKE) != DNSKEY_FLAG_REVOKE)
+ continue;
+
+ if (memcmp(anchor->dnskey.key, revoked_dnskey->dnskey.key, anchor->dnskey.key_size) != 0)
+ continue;
+
+ dns_trust_anchor_remove_revoked(d, anchor);
+ break;
+ }
+ }
+
+ a = hashmap_get(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(revoked_dnskey->key->class, DNS_TYPE_DS, DNS_RESOURCE_KEY_NAME(revoked_dnskey->key)));
+ if (a) {
+ DnsResourceRecord *anchor;
+
+ /* Second, look for DS RRs matching this DNSKEY in our trust anchor database */
+
+ DNS_ANSWER_FOREACH(anchor, a) {
+
+ /* We set mask_revoke to true here, since our
+ * DS fingerprint will be the one of the
+ * unrevoked DNSKEY, but the one we got passed
+ * here has the bit set. */
+ r = dnssec_verify_dnskey_by_ds(revoked_dnskey, anchor, true);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ dns_trust_anchor_remove_revoked(d, anchor);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int dns_trust_anchor_check_revoked(DnsTrustAnchor *d, DnsResourceRecord *dnskey, DnsAnswer *rrs) {
+ DnsResourceRecord *rrsig;
+ int r;
+
+ assert(d);
+ assert(dnskey);
+
+ /* Looks if "dnskey" is a self-signed RR that has been revoked
+ * and matches one of our trust anchor entries. If so, removes
+ * it from the trust anchor and returns > 0. */
+
+ if (dnskey->key->type != DNS_TYPE_DNSKEY)
+ return 0;
+
+ /* Is this DNSKEY revoked? */
+ if ((dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE) == 0)
+ return 0;
+
+ /* Could this be interesting to us at all? If not,
+ * there's no point in looking for and verifying a
+ * self-signed RRSIG. */
+ if (!dns_trust_anchor_knows_domain_positive(d, DNS_RESOURCE_KEY_NAME(dnskey->key)))
+ return 0;
+
+ /* Look for a self-signed RRSIG in the other rrs belonging to this DNSKEY */
+ DNS_ANSWER_FOREACH(rrsig, rrs) {
+ DnssecResult result;
+
+ if (rrsig->key->type != DNS_TYPE_RRSIG)
+ continue;
+
+ r = dnssec_rrsig_match_dnskey(rrsig, dnskey, true);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ r = dnssec_verify_rrset(rrs, dnskey->key, rrsig, dnskey, USEC_INFINITY, &result);
+ if (r < 0)
+ return r;
+ if (result != DNSSEC_VALIDATED)
+ continue;
+
+ /* Bingo! This is a revoked self-signed DNSKEY. Let's
+ * see if this precise one exists in our trust anchor
+ * database, too. */
+ r = dns_trust_anchor_check_revoked_one(d, dnskey);
+ if (r < 0)
+ return r;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+int dns_trust_anchor_is_revoked(DnsTrustAnchor *d, DnsResourceRecord *rr) {
+ assert(d);
+
+ if (!IN_SET(rr->key->type, DNS_TYPE_DS, DNS_TYPE_DNSKEY))
+ return 0;
+
+ return set_contains(d->revoked_by_rr, rr);
+}
diff --git a/src/resolve/resolved-dns-trust-anchor.h b/src/resolve/resolved-dns-trust-anchor.h
new file mode 100644
index 0000000000..5d137faae1
--- /dev/null
+++ b/src/resolve/resolved-dns-trust-anchor.h
@@ -0,0 +1,45 @@
+/*-*- 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/>.
+***/
+
+typedef struct DnsTrustAnchor DnsTrustAnchor;
+
+#include "hashmap.h"
+#include "resolved-dns-answer.h"
+#include "resolved-dns-rr.h"
+
+/* This contains a fixed database mapping domain names to DS or DNSKEY records. */
+
+struct DnsTrustAnchor {
+ Hashmap *positive_by_key;
+ Set *negative_by_name;
+ Set *revoked_by_rr;
+};
+
+int dns_trust_anchor_load(DnsTrustAnchor *d);
+void dns_trust_anchor_flush(DnsTrustAnchor *d);
+
+int dns_trust_anchor_lookup_positive(DnsTrustAnchor *d, const DnsResourceKey* key, DnsAnswer **answer);
+int dns_trust_anchor_lookup_negative(DnsTrustAnchor *d, const char *name);
+
+int dns_trust_anchor_check_revoked(DnsTrustAnchor *d, DnsResourceRecord *dnskey, DnsAnswer *rrs);
+int dns_trust_anchor_is_revoked(DnsTrustAnchor *d, DnsResourceRecord *rr);
diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c
index 8a59bd1c3c..f60b0bddc1 100644
--- a/src/resolve/resolved-dns-zone.c
+++ b/src/resolve/resolved-dns-zone.c
@@ -19,11 +19,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "list.h"
-
-#include "resolved-dns-zone.h"
+#include "alloc-util.h"
#include "dns-domain.h"
+#include "list.h"
#include "resolved-dns-packet.h"
+#include "resolved-dns-zone.h"
+#include "string-util.h"
/* Never allow more than 1K entries */
#define ZONE_MAX 1024
@@ -38,7 +39,7 @@ void dns_zone_item_probe_stop(DnsZoneItem *i) {
t = i->probe_transaction;
i->probe_transaction = NULL;
- set_remove(t->zone_items, i);
+ set_remove(t->notify_zone_items, i);
dns_transaction_gc(t);
}
@@ -162,7 +163,6 @@ static int dns_zone_link_item(DnsZone *z, DnsZoneItem *i) {
}
static int dns_zone_item_probe_start(DnsZoneItem *i) {
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
DnsTransaction *t;
int r;
@@ -171,22 +171,24 @@ static int dns_zone_item_probe_start(DnsZoneItem *i) {
if (i->probe_transaction)
return 0;
- key = dns_resource_key_new(i->rr->key->class, DNS_TYPE_ANY, DNS_RESOURCE_KEY_NAME(i->rr->key));
- if (!key)
- return -ENOMEM;
-
- t = dns_scope_find_transaction(i->scope, key, false);
+ t = dns_scope_find_transaction(i->scope, &DNS_RESOURCE_KEY_CONST(i->rr->key->class, DNS_TYPE_ANY, DNS_RESOURCE_KEY_NAME(i->rr->key)), false);
if (!t) {
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
+
+ key = dns_resource_key_new(i->rr->key->class, DNS_TYPE_ANY, DNS_RESOURCE_KEY_NAME(i->rr->key));
+ if (!key)
+ return -ENOMEM;
+
r = dns_transaction_new(&t, i->scope, key);
if (r < 0)
return r;
}
- r = set_ensure_allocated(&t->zone_items, NULL);
+ r = set_ensure_allocated(&t->notify_zone_items, NULL);
if (r < 0)
goto gc;
- r = set_put(t->zone_items, i);
+ r = set_put(t->notify_zone_items, i);
if (r < 0)
goto gc;
@@ -204,7 +206,7 @@ static int dns_zone_item_probe_start(DnsZoneItem *i) {
}
}
- dns_zone_item_ready(i);
+ dns_zone_item_notify(i);
return 0;
gc:
@@ -221,9 +223,9 @@ int dns_zone_put(DnsZone *z, DnsScope *s, DnsResourceRecord *rr, bool probe) {
assert(s);
assert(rr);
- if (rr->key->class == DNS_CLASS_ANY)
+ if (dns_class_is_pseudo(rr->key->class))
return -EINVAL;
- if (rr->key->type == DNS_TYPE_ANY)
+ if (dns_type_is_pseudo(rr->key->type))
return -EINVAL;
existing = dns_zone_get(z, rr);
@@ -282,97 +284,76 @@ int dns_zone_put(DnsZone *z, DnsScope *s, DnsResourceRecord *rr, bool probe) {
return 0;
}
-int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **ret_answer, DnsAnswer **ret_soa, bool *ret_tentative) {
+int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **ret_answer, DnsAnswer **ret_soa, bool *ret_tentative) {
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
- unsigned i, n_answer = 0, n_soa = 0;
- bool tentative = true;
+ unsigned n_answer = 0;
+ DnsZoneItem *j, *first;
+ bool tentative = true, need_soa = false;
int r;
assert(z);
- assert(q);
+ assert(key);
assert(ret_answer);
- assert(ret_soa);
-
- if (q->n_keys <= 0) {
- *ret_answer = NULL;
- *ret_soa = NULL;
- if (ret_tentative)
- *ret_tentative = false;
+ /* First iteration, count what we have */
- return 0;
- }
+ if (key->type == DNS_TYPE_ANY || key->class == DNS_CLASS_ANY) {
+ bool found = false, added = false;
+ int k;
- /* First iteration, count what we have */
- for (i = 0; i < q->n_keys; i++) {
- DnsZoneItem *j, *first;
+ /* If this is a generic match, then we have to
+ * go through the list by the name and look
+ * for everything manually */
- if (q->keys[i]->type == DNS_TYPE_ANY ||
- q->keys[i]->class == DNS_CLASS_ANY) {
- bool found = false, added = false;
- int k;
+ first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(key));
+ LIST_FOREACH(by_name, j, first) {
+ if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
+ continue;
- /* If this is a generic match, then we have to
- * go through the list by the name and look
- * for everything manually */
+ found = true;
- first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i]));
- LIST_FOREACH(by_name, j, first) {
- if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
- continue;
+ k = dns_resource_key_match_rr(key, j->rr, NULL);
+ if (k < 0)
+ return k;
+ if (k > 0) {
+ n_answer++;
+ added = true;
+ }
- found = true;
+ }
- k = dns_resource_key_match_rr(q->keys[i], j->rr);
- if (k < 0)
- return k;
- if (k > 0) {
- n_answer++;
- added = true;
- }
+ if (found && !added)
+ need_soa = true;
- }
+ } else {
+ bool found = false;
- if (found && !added)
- n_soa++;
+ /* If this is a specific match, then look for
+ * the right key immediately */
- } else {
- bool found = false;
+ first = hashmap_get(z->by_key, key);
+ LIST_FOREACH(by_key, j, first) {
+ if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
+ continue;
- /* If this is a specific match, then look for
- * the right key immediately */
+ found = true;
+ n_answer++;
+ }
- first = hashmap_get(z->by_key, q->keys[i]);
- LIST_FOREACH(by_key, j, first) {
+ if (!found) {
+ first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(key));
+ LIST_FOREACH(by_name, j, first) {
if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
continue;
- found = true;
- n_answer++;
- }
-
- if (!found) {
- first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i]));
- LIST_FOREACH(by_name, j, first) {
- if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
- continue;
-
- n_soa++;
- break;
- }
+ need_soa = true;
+ break;
}
}
}
- if (n_answer <= 0 && n_soa <= 0) {
- *ret_answer = NULL;
- *ret_soa = NULL;
-
- if (ret_tentative)
- *ret_tentative = false;
-
- return 0;
- }
+ if (n_answer <= 0 && !need_soa)
+ goto return_empty;
if (n_answer > 0) {
answer = dns_answer_new(n_answer);
@@ -380,111 +361,122 @@ int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **ret_answer, DnsAnswe
return -ENOMEM;
}
- if (n_soa > 0) {
- soa = dns_answer_new(n_soa);
+ if (need_soa) {
+ soa = dns_answer_new(1);
if (!soa)
return -ENOMEM;
}
/* Second iteration, actually add the RRs to the answers */
- for (i = 0; i < q->n_keys; i++) {
- DnsZoneItem *j, *first;
-
- if (q->keys[i]->type == DNS_TYPE_ANY ||
- q->keys[i]->class == DNS_CLASS_ANY) {
- bool found = false, added = false;
- int k;
-
- first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i]));
- LIST_FOREACH(by_name, j, first) {
- if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
- continue;
-
- found = true;
+ if (key->type == DNS_TYPE_ANY || key->class == DNS_CLASS_ANY) {
+ bool found = false, added = false;
+ int k;
- if (j->state != DNS_ZONE_ITEM_PROBING)
- tentative = false;
+ first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(key));
+ LIST_FOREACH(by_name, j, first) {
+ if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
+ continue;
- k = dns_resource_key_match_rr(q->keys[i], j->rr);
- if (k < 0)
- return k;
- if (k > 0) {
- r = dns_answer_add(answer, j->rr, 0);
- if (r < 0)
- return r;
+ found = true;
- added = true;
- }
- }
+ if (j->state != DNS_ZONE_ITEM_PROBING)
+ tentative = false;
- if (found && !added) {
- r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(q->keys[i]), LLMNR_DEFAULT_TTL);
+ k = dns_resource_key_match_rr(key, j->rr, NULL);
+ if (k < 0)
+ return k;
+ if (k > 0) {
+ r = dns_answer_add(answer, j->rr, 0, DNS_ANSWER_AUTHENTICATED);
if (r < 0)
return r;
+
+ added = true;
}
- } else {
- bool found = false;
+ }
- first = hashmap_get(z->by_key, q->keys[i]);
- LIST_FOREACH(by_key, j, first) {
- if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
- continue;
+ if (found && !added) {
+ r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(key), LLMNR_DEFAULT_TTL);
+ if (r < 0)
+ return r;
+ }
+ } else {
+ bool found = false;
- found = true;
+ first = hashmap_get(z->by_key, key);
+ LIST_FOREACH(by_key, j, first) {
+ if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
+ continue;
- if (j->state != DNS_ZONE_ITEM_PROBING)
- tentative = false;
+ found = true;
- r = dns_answer_add(answer, j->rr, 0);
- if (r < 0)
- return r;
- }
+ if (j->state != DNS_ZONE_ITEM_PROBING)
+ tentative = false;
+
+ r = dns_answer_add(answer, j->rr, 0, DNS_ANSWER_AUTHENTICATED);
+ if (r < 0)
+ return r;
+ }
- if (!found) {
- bool add_soa = false;
+ if (!found) {
+ bool add_soa = false;
- first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i]));
- LIST_FOREACH(by_name, j, first) {
- if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
- continue;
+ first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(key));
+ LIST_FOREACH(by_name, j, first) {
+ if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
+ continue;
- if (j->state != DNS_ZONE_ITEM_PROBING)
- tentative = false;
+ if (j->state != DNS_ZONE_ITEM_PROBING)
+ tentative = false;
- add_soa = true;
- }
+ add_soa = true;
+ }
- if (add_soa) {
- r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(q->keys[i]), LLMNR_DEFAULT_TTL);
- if (r < 0)
- return r;
- }
+ if (add_soa) {
+ r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(key), LLMNR_DEFAULT_TTL);
+ if (r < 0)
+ return r;
}
}
}
+ /* If the caller sets ret_tentative to NULL, then use this as
+ * indication to not return tentative entries */
+
+ if (!ret_tentative && tentative)
+ goto return_empty;
+
*ret_answer = answer;
answer = NULL;
- *ret_soa = soa;
- soa = NULL;
+ if (ret_soa) {
+ *ret_soa = soa;
+ soa = NULL;
+ }
if (ret_tentative)
*ret_tentative = tentative;
return 1;
+
+return_empty:
+ *ret_answer = NULL;
+
+ if (ret_soa)
+ *ret_soa = NULL;
+
+ if (ret_tentative)
+ *ret_tentative = false;
+
+ return 0;
}
void dns_zone_item_conflict(DnsZoneItem *i) {
- _cleanup_free_ char *pretty = NULL;
-
assert(i);
if (!IN_SET(i->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_VERIFYING, DNS_ZONE_ITEM_ESTABLISHED))
return;
- dns_resource_record_to_string(i->rr, &pretty);
- log_info("Detected conflict on %s", strna(pretty));
+ log_info("Detected conflict on %s", strna(dns_resource_record_to_string(i->rr)));
dns_zone_item_probe_stop(i);
@@ -496,16 +488,14 @@ void dns_zone_item_conflict(DnsZoneItem *i) {
manager_next_hostname(i->scope->manager);
}
-void dns_zone_item_ready(DnsZoneItem *i) {
- _cleanup_free_ char *pretty = NULL;
-
+void dns_zone_item_notify(DnsZoneItem *i) {
assert(i);
assert(i->probe_transaction);
if (i->block_ready > 0)
return;
- if (IN_SET(i->probe_transaction->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING))
+ if (IN_SET(i->probe_transaction->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_VALIDATING))
return;
if (i->probe_transaction->state == DNS_TRANSACTION_SUCCESS) {
@@ -535,15 +525,13 @@ void dns_zone_item_ready(DnsZoneItem *i) {
log_debug("Got a successful probe reply, but peer has lexicographically lower IP address and thus lost.");
}
- dns_resource_record_to_string(i->rr, &pretty);
- log_debug("Record %s successfully probed.", strna(pretty));
+ log_debug("Record %s successfully probed.", strna(dns_resource_record_to_string(i->rr)));
dns_zone_item_probe_stop(i);
i->state = DNS_ZONE_ITEM_ESTABLISHED;
}
static int dns_zone_item_verify(DnsZoneItem *i) {
- _cleanup_free_ char *pretty = NULL;
int r;
assert(i);
@@ -551,8 +539,7 @@ static int dns_zone_item_verify(DnsZoneItem *i) {
if (i->state != DNS_ZONE_ITEM_ESTABLISHED)
return 0;
- dns_resource_record_to_string(i->rr, &pretty);
- log_debug("Verifying RR %s", strna(pretty));
+ log_debug("Verifying RR %s", strna(dns_resource_record_to_string(i->rr)));
i->state = DNS_ZONE_ITEM_VERIFYING;
r = dns_zone_item_probe_start(i);
@@ -637,7 +624,6 @@ void dns_zone_verify_all(DnsZone *zone) {
void dns_zone_dump(DnsZone *zone, FILE *f) {
Iterator iterator;
DnsZoneItem *i;
- int r;
if (!zone)
return;
@@ -649,10 +635,10 @@ void dns_zone_dump(DnsZone *zone, FILE *f) {
DnsZoneItem *j;
LIST_FOREACH(by_key, j, i) {
- _cleanup_free_ char *t = NULL;
+ const char *t;
- r = dns_resource_record_to_string(j->rr, &t);
- if (r < 0) {
+ t = dns_resource_record_to_string(j->rr);
+ if (!t) {
log_oom();
continue;
}
diff --git a/src/resolve/resolved-dns-zone.h b/src/resolve/resolved-dns-zone.h
index 495d17cdb1..dbd6a2a368 100644
--- a/src/resolve/resolved-dns-zone.h
+++ b/src/resolve/resolved-dns-zone.h
@@ -31,9 +31,9 @@ typedef struct DnsZone {
typedef struct DnsZoneItem DnsZoneItem;
typedef enum DnsZoneItemState DnsZoneItemState;
-#include "resolved-dns-rr.h"
-#include "resolved-dns-question.h"
#include "resolved-dns-answer.h"
+#include "resolved-dns-question.h"
+#include "resolved-dns-rr.h"
#include "resolved-dns-transaction.h"
/* RFC 4795 Section 2.8. suggests a TTL of 30s by default */
@@ -67,10 +67,10 @@ void dns_zone_flush(DnsZone *z);
int dns_zone_put(DnsZone *z, DnsScope *s, DnsResourceRecord *rr, bool probe);
void dns_zone_remove_rr(DnsZone *z, DnsResourceRecord *rr);
-int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **answer, DnsAnswer **soa, bool *tentative);
+int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **answer, DnsAnswer **soa, bool *tentative);
void dns_zone_item_conflict(DnsZoneItem *i);
-void dns_zone_item_ready(DnsZoneItem *i);
+void dns_zone_item_notify(DnsZoneItem *i);
int dns_zone_check_conflicts(DnsZone *zone, DnsResourceRecord *rr);
int dns_zone_verify_conflicts(DnsZone *zone, DnsResourceKey *key);
diff --git a/src/resolve/resolved-gperf.gperf b/src/resolve/resolved-gperf.gperf
index 8e78fbf06a..82f26215df 100644
--- a/src/resolve/resolved-gperf.gperf
+++ b/src/resolve/resolved-gperf.gperf
@@ -14,6 +14,8 @@ struct ConfigPerfItem;
%struct-type
%includes
%%
-Resolve.DNS, config_parse_dnsv, DNS_SERVER_SYSTEM, 0
-Resolve.FallbackDNS, config_parse_dnsv, DNS_SERVER_FALLBACK, 0
-Resolve.LLMNR, config_parse_support, 0, offsetof(Manager, llmnr_support)
+Resolve.DNS, config_parse_dns_servers, DNS_SERVER_SYSTEM, 0
+Resolve.FallbackDNS, config_parse_dns_servers, DNS_SERVER_FALLBACK, 0
+Resolve.Domains, config_parse_search_domains, 0, 0
+Resolve.LLMNR, config_parse_resolve_support, 0, offsetof(Manager, llmnr_support)
+Resolve.DNSSEC, config_parse_dnssec_mode, 0, offsetof(Manager, dnssec_mode)
diff --git a/src/resolve/resolved-link-bus.c b/src/resolve/resolved-link-bus.c
new file mode 100644
index 0000000000..20352a3e51
--- /dev/null
+++ b/src/resolve/resolved-link-bus.c
@@ -0,0 +1,528 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2016 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 "alloc-util.h"
+#include "bus-util.h"
+#include "parse-util.h"
+#include "resolve-util.h"
+#include "resolved-bus.h"
+#include "resolved-link-bus.h"
+#include "strv.h"
+
+static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_resolve_support, resolve_support, ResolveSupport);
+static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_dnssec_mode, dnssec_mode, DnssecMode);
+
+static int property_get_dns(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Link *l = userdata;
+ DnsServer *s;
+ int r;
+
+ assert(reply);
+ assert(l);
+
+ r = sd_bus_message_open_container(reply, 'a', "(iay)");
+ if (r < 0)
+ return r;
+
+ LIST_FOREACH(servers, s, l->dns_servers) {
+ r = bus_dns_server_append(reply, s, false);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_message_close_container(reply);
+}
+
+static int property_get_domains(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Link *l = userdata;
+ DnsSearchDomain *d;
+ int r;
+
+ assert(reply);
+ assert(l);
+
+ r = sd_bus_message_open_container(reply, 'a', "s");
+ if (r < 0)
+ return r;
+
+ LIST_FOREACH(domains, d, l->search_domains) {
+ r = sd_bus_message_append(reply, "s", d->name);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_message_close_container(reply);
+}
+
+static int property_get_scopes_mask(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Link *l = userdata;
+ uint64_t mask;
+
+ assert(reply);
+ assert(l);
+
+ mask = (l->unicast_scope ? SD_RESOLVED_DNS : 0) |
+ (l->llmnr_ipv4_scope ? SD_RESOLVED_LLMNR_IPV4 : 0) |
+ (l->llmnr_ipv6_scope ? SD_RESOLVED_LLMNR_IPV6 : 0) |
+ (l->mdns_ipv4_scope ? SD_RESOLVED_MDNS_IPV4 : 0) |
+ (l->mdns_ipv6_scope ? SD_RESOLVED_MDNS_IPV6 : 0);
+
+ return sd_bus_message_append(reply, "t", mask);
+}
+
+static int property_get_ntas(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Link *l = userdata;
+ const char *name;
+ Iterator i;
+ int r;
+
+ assert(reply);
+ assert(l);
+
+ r = sd_bus_message_open_container(reply, 'a', "s");
+ if (r < 0)
+ return r;
+
+ SET_FOREACH(name, l->dnssec_negative_trust_anchors, i) {
+ r = sd_bus_message_append(reply, "s", name);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_message_close_container(reply);
+}
+
+static int property_get_dnssec_supported(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Link *l = userdata;
+
+ assert(reply);
+ assert(l);
+
+ return sd_bus_message_append(reply, "b", link_dnssec_supported(l));
+}
+
+int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_free_ struct in_addr_data *dns = NULL;
+ size_t allocated = 0, n = 0;
+ Link *l = userdata;
+ unsigned i;
+ int r;
+
+ assert(message);
+ assert(l);
+
+ r = sd_bus_message_enter_container(message, 'a', "(iay)");
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ int family;
+ size_t sz;
+ const void *d;
+
+ assert_cc(sizeof(int) == sizeof(int32_t));
+
+ r = sd_bus_message_enter_container(message, 'r', "iay");
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ r = sd_bus_message_read(message, "i", &family);
+ if (r < 0)
+ return r;
+
+ if (!IN_SET(family, AF_INET, AF_INET6))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
+
+ r = sd_bus_message_read_array(message, 'y', &d, &sz);
+ if (r < 0)
+ return r;
+ if (sz != FAMILY_ADDRESS_SIZE(family))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
+
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ if (!GREEDY_REALLOC(dns, allocated, n+1))
+ return -ENOMEM;
+
+ dns[n].family = family;
+ memcpy(&dns[n].address, d, sz);
+ n++;
+ }
+
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ dns_server_mark_all(l->dns_servers);
+
+ for (i = 0; i < n; i++) {
+ DnsServer *s;
+
+ s = dns_server_find(l->dns_servers, dns[i].family, &dns[i].address);
+ if (s)
+ dns_server_move_back_and_unmark(s);
+ else {
+ r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, dns[i].family, &dns[i].address);
+ if (r < 0)
+ goto clear;
+ }
+
+ }
+
+ dns_server_unlink_marked(l->dns_servers);
+ link_allocate_scopes(l);
+
+ return sd_bus_reply_method_return(message, NULL);
+
+clear:
+ dns_server_unlink_all(l->dns_servers);
+ return r;
+}
+
+int bus_link_method_set_search_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_free_ char **domains = NULL;
+ Link *l = userdata;
+ char **i;
+ int r;
+
+ assert(message);
+ assert(l);
+
+ r = sd_bus_message_read_strv(message, &domains);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(i, domains) {
+
+ r = dns_name_is_valid(*i);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid search domain %s", *i);
+ if (dns_name_is_root(*i))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Root domain is not suitable as search domain");
+ }
+
+ dns_search_domain_mark_all(l->search_domains);
+
+ STRV_FOREACH(i, domains) {
+ DnsSearchDomain *d;
+
+ r = dns_search_domain_find(l->search_domains, *i, &d);
+ if (r < 0)
+ goto clear;
+
+ if (r > 0)
+ dns_search_domain_move_back_and_unmark(d);
+ else {
+ r = dns_search_domain_new(l->manager, NULL, DNS_SEARCH_DOMAIN_LINK, l, *i);
+ if (r < 0)
+ goto clear;
+ }
+ }
+
+ dns_search_domain_unlink_marked(l->search_domains);
+ return sd_bus_reply_method_return(message, NULL);
+
+clear:
+ dns_search_domain_unlink_all(l->search_domains);
+ return r;
+}
+
+int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Link *l = userdata;
+ ResolveSupport mode;
+ const char *llmnr;
+ int r;
+
+ assert(message);
+ assert(l);
+
+ r = sd_bus_message_read(message, "s", &llmnr);
+ if (r < 0)
+ return r;
+
+ if (isempty(llmnr))
+ mode = RESOLVE_SUPPORT_YES;
+ else {
+ mode = resolve_support_from_string(llmnr);
+ if (mode < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid LLMNR setting: %s", llmnr);
+ }
+
+ l->llmnr_support = mode;
+ link_allocate_scopes(l);
+ link_add_rrs(l, false);
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+int bus_link_method_set_mdns(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Link *l = userdata;
+ ResolveSupport mode;
+ const char *mdns;
+ int r;
+
+ assert(message);
+ assert(l);
+
+ r = sd_bus_message_read(message, "s", &mdns);
+ if (r < 0)
+ return r;
+
+ if (isempty(mdns))
+ mode = RESOLVE_SUPPORT_NO;
+ else {
+ mode = resolve_support_from_string(mdns);
+ if (mode < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid MulticastDNS setting: %s", mdns);
+ }
+
+ l->mdns_support = mode;
+ link_allocate_scopes(l);
+ link_add_rrs(l, false);
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+int bus_link_method_set_dnssec(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Link *l = userdata;
+ const char *dnssec;
+ DnssecMode mode;
+ int r;
+
+ assert(message);
+ assert(l);
+
+ r = sd_bus_message_read(message, "s", &dnssec);
+ if (r < 0)
+ return r;
+
+ if (isempty(dnssec))
+ mode = _DNSSEC_MODE_INVALID;
+ else {
+ mode = dnssec_mode_from_string(dnssec);
+ if (mode < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNSSEC setting: %s", dnssec);
+ }
+
+ link_set_dnssec_mode(l, mode);
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_set_free_free_ Set *ns = NULL;
+ _cleanup_free_ char **ntas = NULL;
+ Link *l = userdata;
+ int r;
+ char **i;
+
+ assert(message);
+ assert(l);
+
+ r = sd_bus_message_read_strv(message, &ntas);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(i, ntas) {
+ r = dns_name_is_valid(*i);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid search negative trust anchor domain: %s", *i);
+ }
+
+ ns = set_new(&dns_name_hash_ops);
+ if (!ns)
+ return -ENOMEM;
+
+ STRV_FOREACH(i, ntas) {
+ r = set_put_strdup(ns, *i);
+ if (r < 0)
+ return r;
+ }
+
+ set_free_free(l->dnssec_negative_trust_anchors);
+ l->dnssec_negative_trust_anchors = ns;
+ ns = NULL;
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+int bus_link_method_revert(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Link *l = userdata;
+
+ assert(message);
+ assert(l);
+
+ link_flush_settings(l);
+ link_allocate_scopes(l);
+ link_add_rrs(l, false);
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+const sd_bus_vtable link_vtable[] = {
+ SD_BUS_VTABLE_START(0),
+
+ SD_BUS_PROPERTY("ScopesMask", "t", property_get_scopes_mask, 0, 0),
+ SD_BUS_PROPERTY("DNS", "a(iay)", property_get_dns, 0, 0),
+ SD_BUS_PROPERTY("Domains", "as", property_get_domains, 0, 0),
+ SD_BUS_PROPERTY("LLMNR", "s", property_get_resolve_support, offsetof(Link, llmnr_support), 0),
+ SD_BUS_PROPERTY("MulticastDNS", "s", property_get_resolve_support, offsetof(Link, mdns_support), 0),
+ SD_BUS_PROPERTY("DNSSEC", "s", property_get_dnssec_mode, offsetof(Link, dnssec_mode), 0),
+ SD_BUS_PROPERTY("DNSSECNegativeTrustAnchors", "as", property_get_ntas, 0, 0),
+ SD_BUS_PROPERTY("DNSSECSupport", "b", property_get_dnssec_supported, 0, 0),
+
+ SD_BUS_METHOD("SetDNS", "a(iay)", NULL, bus_link_method_set_dns_servers, 0),
+ SD_BUS_METHOD("SetDomains", "as", NULL, bus_link_method_set_search_domains, 0),
+ SD_BUS_METHOD("SetLLMNR", "s", NULL, bus_link_method_set_llmnr, 0),
+ SD_BUS_METHOD("SetMulticastDNS", "s", NULL, bus_link_method_set_mdns, 0),
+ SD_BUS_METHOD("SetDNSSEC", "s", NULL, bus_link_method_set_dnssec, 0),
+ SD_BUS_METHOD("SetDNSSECNegativeTrustAnchors", "as", NULL, bus_link_method_set_dnssec_negative_trust_anchors, 0),
+ SD_BUS_METHOD("Revert", NULL, NULL, bus_link_method_revert, 0),
+
+ SD_BUS_VTABLE_END
+};
+
+int link_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
+ _cleanup_free_ char *e = NULL;
+ Manager *m = userdata;
+ int ifindex;
+ Link *link;
+ int r;
+
+ assert(bus);
+ assert(path);
+ assert(interface);
+ assert(found);
+ assert(m);
+
+ r = sd_bus_path_decode(path, "/org/freedesktop/resolve1/link", &e);
+ if (r <= 0)
+ return 0;
+
+ r = parse_ifindex(e, &ifindex);
+ if (r < 0)
+ return 0;
+
+ link = hashmap_get(m->links, INT_TO_PTR(ifindex));
+ if (!link)
+ return 0;
+
+ *found = link;
+ return 1;
+}
+
+char *link_bus_path(Link *link) {
+ _cleanup_free_ char *ifindex = NULL;
+ char *p;
+ int r;
+
+ assert(link);
+
+ if (asprintf(&ifindex, "%i", link->ifindex) < 0)
+ return NULL;
+
+ r = sd_bus_path_encode("/org/freedesktop/resolve1/link", ifindex, &p);
+ if (r < 0)
+ return NULL;
+
+ return p;
+}
+
+int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
+ _cleanup_strv_free_ char **l = NULL;
+ Manager *m = userdata;
+ Link *link;
+ Iterator i;
+ unsigned c = 0;
+
+ assert(bus);
+ assert(path);
+ assert(m);
+ assert(nodes);
+
+ l = new0(char*, hashmap_size(m->links) + 1);
+ if (!l)
+ return -ENOMEM;
+
+ HASHMAP_FOREACH(link, m->links, i) {
+ char *p;
+
+ p = link_bus_path(link);
+ if (!p)
+ return -ENOMEM;
+
+ l[c++] = p;
+ }
+
+ l[c] = NULL;
+ *nodes = l;
+ l = NULL;
+
+ return 1;
+}
diff --git a/src/resolve/resolved-link-bus.h b/src/resolve/resolved-link-bus.h
new file mode 100644
index 0000000000..d444957d1c
--- /dev/null
+++ b/src/resolve/resolved-link-bus.h
@@ -0,0 +1,40 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2016 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"
+
+#include "resolved-link.h"
+
+extern const sd_bus_vtable link_vtable[];
+
+int link_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
+char *link_bus_path(Link *link);
+int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
+
+int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_link_method_set_search_domains(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_link_method_set_mdns(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_link_method_set_dnssec(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_link_method_revert(sd_bus_message *message, void *userdata, sd_bus_error *error);
diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c
index b9fd8e3dbc..b203f19dbb 100644
--- a/src/resolve/resolved-link.c
+++ b/src/resolve/resolved-link.c
@@ -22,9 +22,13 @@
#include <net/if.h>
#include "sd-network.h"
-#include "strv.h"
+
+#include "alloc-util.h"
#include "missing.h"
+#include "parse-util.h"
#include "resolved-link.h"
+#include "string-util.h"
+#include "strv.h"
int link_new(Manager *m, Link **ret, int ifindex) {
_cleanup_(link_freep) Link *l = NULL;
@@ -42,7 +46,9 @@ int link_new(Manager *m, Link **ret, int ifindex) {
return -ENOMEM;
l->ifindex = ifindex;
- l->llmnr_support = SUPPORT_YES;
+ l->llmnr_support = RESOLVE_SUPPORT_YES;
+ l->mdns_support = RESOLVE_SUPPORT_NO;
+ l->dnssec_mode = _DNSSEC_MODE_INVALID;
r = hashmap_put(m->links, INT_TO_PTR(ifindex), l);
if (r < 0)
@@ -57,37 +63,48 @@ int link_new(Manager *m, Link **ret, int ifindex) {
return 0;
}
+void link_flush_settings(Link *l) {
+ assert(l);
+
+ l->llmnr_support = RESOLVE_SUPPORT_YES;
+ l->mdns_support = RESOLVE_SUPPORT_NO;
+ l->dnssec_mode = _DNSSEC_MODE_INVALID;
+
+ dns_server_unlink_all(l->dns_servers);
+ dns_search_domain_unlink_all(l->search_domains);
+
+ l->dnssec_negative_trust_anchors = set_free_free(l->dnssec_negative_trust_anchors);
+}
+
Link *link_free(Link *l) {
if (!l)
return NULL;
+ link_flush_settings(l);
+
while (l->addresses)
- link_address_free(l->addresses);
+ (void) link_address_free(l->addresses);
if (l->manager)
hashmap_remove(l->manager->links, INT_TO_PTR(l->ifindex));
- while (l->dns_servers) {
- DnsServer *s = l->dns_servers;
-
- LIST_REMOVE(servers, l->dns_servers, s);
- dns_server_unref(s);
- }
-
dns_scope_free(l->unicast_scope);
dns_scope_free(l->llmnr_ipv4_scope);
dns_scope_free(l->llmnr_ipv6_scope);
+ dns_scope_free(l->mdns_ipv4_scope);
+ dns_scope_free(l->mdns_ipv6_scope);
free(l);
return NULL;
}
-static void link_allocate_scopes(Link *l) {
+void link_allocate_scopes(Link *l) {
int r;
assert(l);
- if (l->dns_servers) {
+ if (link_relevant(l, AF_UNSPEC, false) &&
+ l->dns_servers) {
if (!l->unicast_scope) {
r = dns_scope_new(l->manager, &l->unicast_scope, l, DNS_PROTOCOL_DNS, AF_UNSPEC);
if (r < 0)
@@ -96,9 +113,9 @@ static void link_allocate_scopes(Link *l) {
} else
l->unicast_scope = dns_scope_free(l->unicast_scope);
- if (link_relevant(l, AF_INET) &&
- l->llmnr_support != SUPPORT_NO &&
- l->manager->llmnr_support != SUPPORT_NO) {
+ if (link_relevant(l, AF_INET, true) &&
+ l->llmnr_support != RESOLVE_SUPPORT_NO &&
+ l->manager->llmnr_support != RESOLVE_SUPPORT_NO) {
if (!l->llmnr_ipv4_scope) {
r = dns_scope_new(l->manager, &l->llmnr_ipv4_scope, l, DNS_PROTOCOL_LLMNR, AF_INET);
if (r < 0)
@@ -107,9 +124,9 @@ static void link_allocate_scopes(Link *l) {
} else
l->llmnr_ipv4_scope = dns_scope_free(l->llmnr_ipv4_scope);
- if (link_relevant(l, AF_INET6) &&
- l->llmnr_support != SUPPORT_NO &&
- l->manager->llmnr_support != SUPPORT_NO &&
+ if (link_relevant(l, AF_INET6, true) &&
+ l->llmnr_support != RESOLVE_SUPPORT_NO &&
+ l->manager->llmnr_support != RESOLVE_SUPPORT_NO &&
socket_ipv6_is_supported()) {
if (!l->llmnr_ipv6_scope) {
r = dns_scope_new(l->manager, &l->llmnr_ipv6_scope, l, DNS_PROTOCOL_LLMNR, AF_INET6);
@@ -118,6 +135,28 @@ static void link_allocate_scopes(Link *l) {
}
} else
l->llmnr_ipv6_scope = dns_scope_free(l->llmnr_ipv6_scope);
+
+ if (link_relevant(l, AF_INET, true) &&
+ l->mdns_support != RESOLVE_SUPPORT_NO &&
+ l->manager->mdns_support != RESOLVE_SUPPORT_NO) {
+ if (!l->mdns_ipv4_scope) {
+ r = dns_scope_new(l->manager, &l->mdns_ipv4_scope, l, DNS_PROTOCOL_MDNS, AF_INET);
+ if (r < 0)
+ log_warning_errno(r, "Failed to allocate mDNS IPv4 scope: %m");
+ }
+ } else
+ l->mdns_ipv4_scope = dns_scope_free(l->mdns_ipv4_scope);
+
+ if (link_relevant(l, AF_INET6, true) &&
+ l->mdns_support != RESOLVE_SUPPORT_NO &&
+ l->manager->mdns_support != RESOLVE_SUPPORT_NO) {
+ if (!l->mdns_ipv6_scope) {
+ r = dns_scope_new(l->manager, &l->mdns_ipv6_scope, l, DNS_PROTOCOL_MDNS, AF_INET6);
+ if (r < 0)
+ log_warning_errno(r, "Failed to allocate mDNS IPv6 scope: %m");
+ }
+ } else
+ l->mdns_ipv6_scope = dns_scope_free(l->mdns_ipv6_scope);
}
void link_add_rrs(Link *l, bool force_remove) {
@@ -154,29 +193,32 @@ int link_update_rtnl(Link *l, sd_netlink_message *m) {
static int link_update_dns_servers(Link *l) {
_cleanup_strv_free_ char **nameservers = NULL;
char **nameserver;
- DnsServer *s, *nx;
int r;
assert(l);
r = sd_network_link_get_dns(l->ifindex, &nameservers);
+ if (r == -ENODATA) {
+ r = 0;
+ goto clear;
+ }
if (r < 0)
goto clear;
- LIST_FOREACH(servers, s, l->dns_servers)
- s->marked = true;
+ dns_server_mark_all(l->dns_servers);
STRV_FOREACH(nameserver, nameservers) {
union in_addr_union a;
+ DnsServer *s;
int family;
r = in_addr_from_string_auto(*nameserver, &family, &a);
if (r < 0)
goto clear;
- s = link_find_dns_server(l, family, &a);
+ s = dns_server_find(l->dns_servers, family, &a);
if (s)
- s->marked = false;
+ dns_server_move_back_and_unmark(s);
else {
r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a);
if (r < 0)
@@ -184,104 +226,300 @@ static int link_update_dns_servers(Link *l) {
}
}
- LIST_FOREACH_SAFE(servers, s, nx, l->dns_servers)
- if (s->marked) {
- LIST_REMOVE(servers, l->dns_servers, s);
- dns_server_unref(s);
- }
-
+ dns_server_unlink_marked(l->dns_servers);
return 0;
clear:
- while (l->dns_servers) {
- s = l->dns_servers;
+ dns_server_unlink_all(l->dns_servers);
+ return r;
+}
+
+static int link_update_llmnr_support(Link *l) {
+ _cleanup_free_ char *b = NULL;
+ int r;
+
+ assert(l);
+
+ r = sd_network_link_get_llmnr(l->ifindex, &b);
+ if (r == -ENODATA) {
+ r = 0;
+ goto clear;
+ }
+ if (r < 0)
+ goto clear;
- LIST_REMOVE(servers, l->dns_servers, s);
- dns_server_unref(s);
+ l->llmnr_support = resolve_support_from_string(b);
+ if (l->llmnr_support < 0) {
+ r = -EINVAL;
+ goto clear;
}
+ return 0;
+
+clear:
+ l->llmnr_support = RESOLVE_SUPPORT_YES;
return r;
}
-static int link_update_llmnr_support(Link *l) {
+static int link_update_mdns_support(Link *l) {
_cleanup_free_ char *b = NULL;
int r;
assert(l);
- r = sd_network_link_get_llmnr(l->ifindex, &b);
+ r = sd_network_link_get_mdns(l->ifindex, &b);
+ if (r == -ENODATA) {
+ r = 0;
+ goto clear;
+ }
if (r < 0)
goto clear;
- r = parse_boolean(b);
- if (r < 0) {
- if (streq(b, "resolve"))
- l->llmnr_support = SUPPORT_RESOLVE;
- else
- goto clear;
+ l->mdns_support = resolve_support_from_string(b);
+ if (l->mdns_support < 0) {
+ r = -EINVAL;
+ goto clear;
+ }
+
+ return 0;
+
+clear:
+ l->mdns_support = RESOLVE_SUPPORT_NO;
+ return r;
+}
+
+void link_set_dnssec_mode(Link *l, DnssecMode mode) {
+
+ assert(l);
+
+ if (l->dnssec_mode == mode)
+ return;
+
+ if ((l->dnssec_mode == _DNSSEC_MODE_INVALID) ||
+ (l->dnssec_mode == DNSSEC_NO && mode != DNSSEC_NO) ||
+ (l->dnssec_mode == DNSSEC_ALLOW_DOWNGRADE && mode == DNSSEC_YES)) {
+
+ /* When switching from non-DNSSEC mode to DNSSEC mode, flush the cache. Also when switching from the
+ * allow-downgrade mode to full DNSSEC mode, flush it too. */
+ if (l->unicast_scope)
+ dns_cache_flush(&l->unicast_scope->cache);
+ }
+
+ l->dnssec_mode = mode;
+}
+
+static int link_update_dnssec_mode(Link *l) {
+ _cleanup_free_ char *m = NULL;
+ DnssecMode mode;
+ int r;
+
+ assert(l);
+
+ r = sd_network_link_get_dnssec(l->ifindex, &m);
+ if (r == -ENODATA) {
+ r = 0;
+ goto clear;
+ }
+ if (r < 0)
+ goto clear;
+
+ mode = dnssec_mode_from_string(m);
+ if (mode < 0) {
+ r = -EINVAL;
+ goto clear;
+ }
+
+ link_set_dnssec_mode(l, mode);
+
+ return 0;
+
+clear:
+ l->dnssec_mode = _DNSSEC_MODE_INVALID;
+ return r;
+}
+
+static int link_update_dnssec_negative_trust_anchors(Link *l) {
+ _cleanup_strv_free_ char **ntas = NULL;
+ _cleanup_set_free_free_ Set *ns = NULL;
+ char **i;
+ int r;
+
+ assert(l);
- } else if (r > 0)
- l->llmnr_support = SUPPORT_YES;
- else
- l->llmnr_support = SUPPORT_NO;
+ r = sd_network_link_get_dnssec_negative_trust_anchors(l->ifindex, &ntas);
+ if (r == -ENODATA) {
+ r = 0;
+ goto clear;
+ }
+ if (r < 0)
+ goto clear;
+
+ ns = set_new(&dns_name_hash_ops);
+ if (!ns)
+ return -ENOMEM;
+
+ STRV_FOREACH(i, ntas) {
+ r = set_put_strdup(ns, *i);
+ if (r < 0)
+ return r;
+ }
+
+ set_free_free(l->dnssec_negative_trust_anchors);
+ l->dnssec_negative_trust_anchors = ns;
+ ns = NULL;
return 0;
clear:
- l->llmnr_support = SUPPORT_YES;
+ l->dnssec_negative_trust_anchors = set_free_free(l->dnssec_negative_trust_anchors);
return r;
}
-static int link_update_domains(Link *l) {
+static int link_update_search_domains(Link *l) {
+ _cleanup_strv_free_ char **domains = NULL;
+ char **i;
int r;
- if (!l->unicast_scope)
- return 0;
+ assert(l);
- l->unicast_scope->domains = strv_free(l->unicast_scope->domains);
+ r = sd_network_link_get_domains(l->ifindex, &domains);
+ if (r == -ENODATA) {
+ /* networkd knows nothing about this interface, and that's fine. */
+ r = 0;
+ goto clear;
+ }
+ if (r < 0)
+ goto clear;
- r = sd_network_link_get_domains(l->ifindex,
- &l->unicast_scope->domains);
+ dns_search_domain_mark_all(l->search_domains);
+
+ STRV_FOREACH(i, domains) {
+ DnsSearchDomain *d;
+
+ r = dns_search_domain_find(l->search_domains, *i, &d);
+ if (r < 0)
+ goto clear;
+
+ if (r > 0)
+ dns_search_domain_move_back_and_unmark(d);
+ else {
+ r = dns_search_domain_new(l->manager, NULL, DNS_SEARCH_DOMAIN_LINK, l, *i);
+ if (r < 0)
+ goto clear;
+ }
+ }
+
+ dns_search_domain_unlink_marked(l->search_domains);
+ return 0;
+
+clear:
+ dns_search_domain_unlink_all(l->search_domains);
+ return r;
+}
+
+static int link_is_unmanaged(Link *l) {
+ _cleanup_free_ char *state = NULL;
+ int r;
+
+ assert(l);
+
+ r = sd_network_link_get_setup_state(l->ifindex, &state);
+ if (r == -ENODATA)
+ return 1;
if (r < 0)
return r;
- return 0;
+ return STR_IN_SET(state, "pending", "unmanaged");
+}
+
+static void link_read_settings(Link *l) {
+ int r;
+
+ assert(l);
+
+ /* Read settings from networkd, except when networkd is not managing this interface. */
+
+ r = link_is_unmanaged(l);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to determine whether interface %s is managed: %m", l->name);
+ return;
+ }
+ if (r > 0) {
+
+ /* If this link used to be managed, but is now unmanaged, flush all our settings -- but only once. */
+ if (l->is_managed)
+ link_flush_settings(l);
+
+ l->is_managed = false;
+ return;
+ }
+
+ l->is_managed = true;
+
+ r = link_update_dns_servers(l);
+ if (r < 0)
+ log_warning_errno(r, "Failed to read DNS servers for interface %s, ignoring: %m", l->name);
+
+ r = link_update_llmnr_support(l);
+ if (r < 0)
+ log_warning_errno(r, "Failed to read LLMNR support for interface %s, ignoring: %m", l->name);
+
+ r = link_update_mdns_support(l);
+ if (r < 0)
+ log_warning_errno(r, "Failed to read mDNS support for interface %s, ignoring: %m", l->name);
+
+ r = link_update_dnssec_mode(l);
+ if (r < 0)
+ log_warning_errno(r, "Failed to read DNSSEC mode for interface %s, ignoring: %m", l->name);
+
+ r = link_update_dnssec_negative_trust_anchors(l);
+ if (r < 0)
+ log_warning_errno(r, "Failed to read DNSSEC negative trust anchors for interface %s, ignoring: %m", l->name);
+
+ r = link_update_search_domains(l);
+ if (r < 0)
+ log_warning_errno(r, "Failed to read search domains for interface %s, ignoring: %m", l->name);
}
int link_update_monitor(Link *l) {
assert(l);
- link_update_dns_servers(l);
- link_update_llmnr_support(l);
+ link_read_settings(l);
link_allocate_scopes(l);
- link_update_domains(l);
link_add_rrs(l, false);
return 0;
}
-bool link_relevant(Link *l, int family) {
+bool link_relevant(Link *l, int family, bool multicast) {
_cleanup_free_ char *state = NULL;
LinkAddress *a;
assert(l);
- /* A link is relevant if it isn't a loopback or pointopoint
- * device, has a link beat, can do multicast and has at least
- * one relevant IP address */
+ /* A link is relevant for multicast traffic if it isn't a loopback or pointopoint device, has a link beat, can
+ * do multicast and has at least one relevant IP address */
- if (l->flags & (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_DORMANT))
+ if (l->flags & (IFF_LOOPBACK|IFF_DORMANT))
return false;
- if ((l->flags & (IFF_UP|IFF_LOWER_UP|IFF_MULTICAST)) != (IFF_UP|IFF_LOWER_UP|IFF_MULTICAST))
+ if ((l->flags & (IFF_UP|IFF_LOWER_UP)) != (IFF_UP|IFF_LOWER_UP))
return false;
+ if (multicast) {
+ if (l->flags & IFF_POINTOPOINT)
+ return false;
+
+ if ((l->flags & IFF_MULTICAST) != IFF_MULTICAST)
+ return false;
+ }
+
sd_network_link_get_operational_state(l->ifindex, &state);
if (state && !STR_IN_SET(state, "unknown", "degraded", "routable"))
return false;
LIST_FOREACH(addresses, a, l->addresses)
- if (a->family == family && link_address_relevant(a))
+ if ((family == AF_UNSPEC || a->family == family) && link_address_relevant(a))
return true;
return false;
@@ -299,31 +537,17 @@ LinkAddress *link_find_address(Link *l, int family, const union in_addr_union *i
return NULL;
}
-DnsServer* link_find_dns_server(Link *l, int family, const union in_addr_union *in_addr) {
- DnsServer *s;
-
- assert(l);
-
- LIST_FOREACH(servers, s, l->dns_servers)
- if (s->family == family && in_addr_equal(family, &s->address, in_addr))
- return s;
- return NULL;
-}
-
DnsServer* link_set_dns_server(Link *l, DnsServer *s) {
assert(l);
if (l->current_dns_server == s)
return s;
- if (s) {
- _cleanup_free_ char *ip = NULL;
+ if (s)
+ log_info("Switching to DNS server %s for interface %s.", dns_server_string(s), l->name);
- in_addr_to_string(s->family, &s->address, &ip);
- log_info("Switching to DNS server %s for interface %s.", strna(ip), l->name);
- }
-
- l->current_dns_server = s;
+ dns_server_unref(l->current_dns_server);
+ l->current_dns_server = dns_server_ref(s);
if (l->unicast_scope)
dns_cache_flush(&l->unicast_scope->cache);
@@ -346,7 +570,9 @@ void link_next_dns_server(Link *l) {
if (!l->current_dns_server)
return;
- if (l->current_dns_server->servers_next) {
+ /* Change to the next one, but make sure to follow the linked
+ * list only if this server is actually still linked. */
+ if (l->current_dns_server->linked && l->current_dns_server->servers_next) {
link_set_dns_server(l, l->current_dns_server->servers_next);
return;
}
@@ -354,6 +580,30 @@ void link_next_dns_server(Link *l) {
link_set_dns_server(l, l->dns_servers);
}
+DnssecMode link_get_dnssec_mode(Link *l) {
+ assert(l);
+
+ if (l->dnssec_mode != _DNSSEC_MODE_INVALID)
+ return l->dnssec_mode;
+
+ return manager_get_dnssec_mode(l->manager);
+}
+
+bool link_dnssec_supported(Link *l) {
+ DnsServer *server;
+
+ assert(l);
+
+ if (link_get_dnssec_mode(l) == DNSSEC_NO)
+ return false;
+
+ server = link_get_dns_server(l);
+ if (server)
+ return dns_server_dnssec_supported(server);
+
+ return true;
+}
+
int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr) {
LinkAddress *a;
@@ -415,8 +665,8 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) {
if (!force_remove &&
link_address_relevant(a) &&
a->link->llmnr_ipv4_scope &&
- a->link->llmnr_support == SUPPORT_YES &&
- a->link->manager->llmnr_support == SUPPORT_YES) {
+ a->link->llmnr_support == RESOLVE_SUPPORT_YES &&
+ a->link->manager->llmnr_support == RESOLVE_SUPPORT_YES) {
if (!a->link->manager->llmnr_host_ipv4_key) {
a->link->manager->llmnr_host_ipv4_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, a->link->manager->llmnr_hostname);
@@ -472,8 +722,8 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) {
if (!force_remove &&
link_address_relevant(a) &&
a->link->llmnr_ipv6_scope &&
- a->link->llmnr_support == SUPPORT_YES &&
- a->link->manager->llmnr_support == SUPPORT_YES) {
+ a->link->llmnr_support == RESOLVE_SUPPORT_YES &&
+ a->link->manager->llmnr_support == RESOLVE_SUPPORT_YES) {
if (!a->link->manager->llmnr_host_ipv6_key) {
a->link->manager->llmnr_host_ipv6_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, a->link->manager->llmnr_hostname);
diff --git a/src/resolve/resolved-link.h b/src/resolve/resolved-link.h
index e3ab27c249..6544214b77 100644
--- a/src/resolve/resolved-link.h
+++ b/src/resolve/resolved-link.h
@@ -25,13 +25,19 @@
#include "in-addr-util.h"
#include "ratelimit.h"
+#include "resolve-util.h"
typedef struct Link Link;
typedef struct LinkAddress LinkAddress;
#include "resolved-dns-rr.h"
+#include "resolved-dns-search-domain.h"
+#include "resolved-dns-server.h"
#include "resolved-manager.h"
+#define LINK_SEARCH_DOMAINS_MAX 32
+#define LINK_DNS_SERVERS_MAX 32
+
struct LinkAddress {
Link *link;
@@ -56,12 +62,23 @@ struct Link {
LIST_HEAD(DnsServer, dns_servers);
DnsServer *current_dns_server;
+ unsigned n_dns_servers;
+
+ LIST_HEAD(DnsSearchDomain, search_domains);
+ unsigned n_search_domains;
- Support llmnr_support;
+ ResolveSupport llmnr_support;
+ ResolveSupport mdns_support;
+ DnssecMode dnssec_mode;
+ Set *dnssec_negative_trust_anchors;
DnsScope *unicast_scope;
DnsScope *llmnr_ipv4_scope;
DnsScope *llmnr_ipv6_scope;
+ DnsScope *mdns_ipv4_scope;
+ DnsScope *mdns_ipv6_scope;
+
+ bool is_managed;
char name[IF_NAMESIZE];
uint32_t mtu;
@@ -71,15 +88,21 @@ int link_new(Manager *m, Link **ret, int ifindex);
Link *link_free(Link *l);
int link_update_rtnl(Link *l, sd_netlink_message *m);
int link_update_monitor(Link *l);
-bool link_relevant(Link *l, int family);
+bool link_relevant(Link *l, int family, bool multicast);
LinkAddress* link_find_address(Link *l, int family, const union in_addr_union *in_addr);
void link_add_rrs(Link *l, bool force_remove);
+void link_flush_settings(Link *l);
+void link_set_dnssec_mode(Link *l, DnssecMode mode);
+void link_allocate_scopes(Link *l);
+
DnsServer* link_set_dns_server(Link *l, DnsServer *s);
-DnsServer* link_find_dns_server(Link *l, int family, const union in_addr_union *in_addr);
DnsServer* link_get_dns_server(Link *l);
void link_next_dns_server(Link *l);
+DnssecMode link_get_dnssec_mode(Link *l);
+bool link_dnssec_supported(Link *l);
+
int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr);
LinkAddress *link_address_free(LinkAddress *a);
int link_address_update_rtnl(LinkAddress *a, sd_netlink_message *m);
diff --git a/src/resolve/resolved-llmnr.c b/src/resolve/resolved-llmnr.c
index 8afaf8db6e..f52ab8f384 100644
--- a/src/resolve/resolved-llmnr.c
+++ b/src/resolve/resolved-llmnr.c
@@ -19,11 +19,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <resolv.h>
#include <netinet/in.h>
+#include <resolv.h>
-#include "resolved-manager.h"
+#include "fd-util.h"
#include "resolved-llmnr.h"
+#include "resolved-manager.h"
void manager_llmnr_stop(Manager *m) {
assert(m);
@@ -46,7 +47,7 @@ int manager_llmnr_start(Manager *m) {
assert(m);
- if (m->llmnr_support == SUPPORT_NO)
+ if (m->llmnr_support == RESOLVE_SUPPORT_NO)
return 0;
r = manager_llmnr_ipv4_udp_fd(m);
@@ -79,7 +80,7 @@ int manager_llmnr_start(Manager *m) {
eaddrinuse:
log_warning("There appears to be another LLMNR responder running. Turning off LLMNR support.");
- m->llmnr_support = SUPPORT_NO;
+ m->llmnr_support = RESOLVE_SUPPORT_NO;
manager_llmnr_stop(m);
return 0;
@@ -116,7 +117,7 @@ static int on_llmnr_packet(sd_event_source *s, int fd, uint32_t revents, void *u
dns_scope_process_query(scope, NULL, p);
} else
- log_debug("Invalid LLMNR UDP packet.");
+ log_debug("Invalid LLMNR UDP packet, ignoring.");
return 0;
}
@@ -192,6 +193,8 @@ int manager_llmnr_ipv4_udp_fd(Manager *m) {
if (r < 0)
goto fail;
+ (void) sd_event_source_set_description(m->llmnr_ipv4_udp_event_source, "llmnr-ipv4-udp");
+
return m->llmnr_ipv4_udp_fd;
fail:
@@ -266,10 +269,10 @@ int manager_llmnr_ipv6_udp_fd(Manager *m) {
}
r = sd_event_add_io(m->event, &m->llmnr_ipv6_udp_event_source, m->llmnr_ipv6_udp_fd, EPOLLIN, on_llmnr_packet, m);
- if (r < 0) {
- r = -errno;
+ if (r < 0)
goto fail;
- }
+
+ (void) sd_event_source_set_description(m->llmnr_ipv6_udp_event_source, "llmnr-ipv6-udp");
return m->llmnr_ipv6_udp_fd;
@@ -392,6 +395,8 @@ int manager_llmnr_ipv4_tcp_fd(Manager *m) {
if (r < 0)
goto fail;
+ (void) sd_event_source_set_description(m->llmnr_ipv4_tcp_event_source, "llmnr-ipv4-tcp");
+
return m->llmnr_ipv4_tcp_fd;
fail:
@@ -460,10 +465,10 @@ int manager_llmnr_ipv6_tcp_fd(Manager *m) {
}
r = sd_event_add_io(m->event, &m->llmnr_ipv6_tcp_event_source, m->llmnr_ipv6_tcp_fd, EPOLLIN, on_llmnr_stream, m);
- if (r < 0) {
- r = -errno;
+ if (r < 0)
goto fail;
- }
+
+ (void) sd_event_source_set_description(m->llmnr_ipv6_tcp_event_source, "llmnr-ipv6-tcp");
return m->llmnr_ipv6_tcp_fd;
diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c
index de924e3ed9..d6d75a3f78 100644
--- a/src/resolve/resolved-manager.c
+++ b/src/resolve/resolved-manager.c
@@ -19,26 +19,32 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <resolv.h>
-#include <sys/ioctl.h>
-#include <poll.h>
#include <netinet/in.h>
+#include <poll.h>
+#include <sys/ioctl.h>
-#include "netlink-util.h"
-#include "network-internal.h"
-#include "socket-util.h"
#include "af-list.h"
-#include "utf8.h"
+#include "alloc-util.h"
+#include "dns-domain.h"
+#include "fd-util.h"
#include "fileio-label.h"
+#include "hostname-util.h"
+#include "io-util.h"
+#include "netlink-util.h"
+#include "network-internal.h"
#include "ordered-set.h"
+#include "parse-util.h"
#include "random-util.h"
-#include "hostname-util.h"
-
-#include "dns-domain.h"
-#include "resolved-conf.h"
#include "resolved-bus.h"
-#include "resolved-manager.h"
+#include "resolved-conf.h"
#include "resolved-llmnr.h"
+#include "resolved-manager.h"
+#include "resolved-resolv-conf.h"
+#include "resolved-mdns.h"
+#include "socket-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "utf8.h"
#define SEND_TIMEOUT_USEC (200 * USEC_PER_MSEC)
@@ -188,7 +194,7 @@ fail:
}
static int manager_rtnl_listen(Manager *m) {
- _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
sd_netlink_message *i;
int r;
@@ -307,6 +313,8 @@ static int manager_network_monitor_listen(Manager *m) {
if (r < 0)
return r;
+ (void) sd_event_source_set_description(m->network_event_source, "network-monitor");
+
return 0;
}
@@ -346,7 +354,7 @@ static int determine_hostname(char **llmnr_hostname, char **mdns_hostname) {
return -EINVAL;
}
- r = dns_label_escape(label, r, &n);
+ r = dns_label_escape_new(label, r, &n);
if (r < 0)
return log_error_errno(r, "Failed to escape host name: %m");
@@ -414,6 +422,8 @@ static int manager_watch_hostname(Manager *m) {
return log_error_errno(r, "Failed to add hostname event source: %m");
}
+ (void) sd_event_source_set_description(m->hostname_event_source, "hostname");
+
r = determine_hostname(&m->llmnr_hostname, &m->mdns_hostname);
if (r < 0) {
log_info("Defaulting to hostname 'linux'.");
@@ -467,12 +477,20 @@ int manager_new(Manager **ret) {
m->llmnr_ipv4_udp_fd = m->llmnr_ipv6_udp_fd = -1;
m->llmnr_ipv4_tcp_fd = m->llmnr_ipv6_tcp_fd = -1;
+ m->mdns_ipv4_fd = m->mdns_ipv6_fd = -1;
m->hostname_fd = -1;
- m->llmnr_support = SUPPORT_YES;
+ m->llmnr_support = RESOLVE_SUPPORT_YES;
+ m->mdns_support = RESOLVE_SUPPORT_NO;
+ m->dnssec_mode = DNSSEC_NO;
m->read_resolv_conf = true;
+ m->need_builtin_fallbacks = true;
+
+ r = dns_trust_anchor_load(&m->trust_anchor);
+ if (r < 0)
+ return r;
- r = manager_parse_dns_server(m, DNS_SERVER_FALLBACK, DNS_SERVERS);
+ r = manager_parse_config_file(m);
if (r < 0)
return r;
@@ -522,6 +540,10 @@ int manager_start(Manager *m) {
if (r < 0)
return r;
+ r = manager_mdns_start(m);
+ if (r < 0)
+ return r;
+
return 0;
}
@@ -531,15 +553,16 @@ Manager *manager_free(Manager *m) {
if (!m)
return NULL;
+ dns_server_unlink_all(m->dns_servers);
+ dns_server_unlink_all(m->fallback_dns_servers);
+ dns_search_domain_unlink_all(m->search_domains);
+
while ((l = hashmap_first(m->links)))
link_free(l);
while (m->dns_queries)
dns_query_free(m->dns_queries);
- 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);
@@ -548,7 +571,11 @@ Manager *manager_free(Manager *m) {
sd_event_source_unref(m->network_event_source);
sd_network_monitor_unref(m->network_monitor);
+ sd_netlink_unref(m->rtnl);
+ sd_event_source_unref(m->rtnl_event_source);
+
manager_llmnr_stop(m);
+ manager_mdns_stop(m);
sd_bus_slot_unref(m->prepare_for_sleep_slot);
sd_event_source_unref(m->bus_retry_event_source);
@@ -566,299 +593,13 @@ Manager *manager_free(Manager *m) {
free(m->llmnr_hostname);
free(m->mdns_hostname);
+ dns_trust_anchor_flush(&m->trust_anchor);
+
free(m);
return NULL;
}
-int manager_read_resolv_conf(Manager *m) {
- _cleanup_fclose_ FILE *f = NULL;
- struct stat st, own;
- char line[LINE_MAX];
- DnsServer *s, *nx;
- usec_t t;
- int r;
-
- assert(m);
-
- /* Reads the system /etc/resolv.conf, if it exists and is not
- * symlinked to our own resolv.conf instance */
-
- if (!m->read_resolv_conf)
- return 0;
-
- r = stat("/etc/resolv.conf", &st);
- if (r < 0) {
- if (errno != ENOENT)
- log_warning_errno(errno, "Failed to open /etc/resolv.conf: %m");
- r = -errno;
- goto clear;
- }
-
- /* Have we already seen the file? */
- t = timespec_load(&st.st_mtim);
- if (t == m->resolv_conf_mtime)
- return 0;
-
- m->resolv_conf_mtime = t;
-
- /* Is it symlinked to our own file? */
- if (stat("/run/systemd/resolve/resolv.conf", &own) >= 0 &&
- st.st_dev == own.st_dev &&
- st.st_ino == own.st_ino) {
- r = 0;
- goto clear;
- }
-
- f = fopen("/etc/resolv.conf", "re");
- if (!f) {
- if (errno != ENOENT)
- log_warning_errno(errno, "Failed to open /etc/resolv.conf: %m");
- r = -errno;
- goto clear;
- }
-
- if (fstat(fileno(f), &st) < 0) {
- r = log_error_errno(errno, "Failed to stat open file: %m");
- goto clear;
- }
-
- LIST_FOREACH(servers, s, m->dns_servers)
- s->marked = true;
-
- FOREACH_LINE(line, f, r = -errno; goto clear) {
- union in_addr_union address;
- int family;
- char *l;
- const char *a;
-
- truncate_nl(line);
-
- l = strstrip(line);
- if (*l == '#' || *l == ';')
- continue;
-
- a = first_word(l, "nameserver");
- if (!a)
- continue;
-
- r = in_addr_from_string_auto(a, &family, &address);
- if (r < 0) {
- log_warning("Failed to parse name server %s.", a);
- continue;
- }
-
- LIST_FOREACH(servers, s, m->dns_servers)
- if (s->family == family && in_addr_equal(family, &s->address, &address) > 0)
- break;
-
- if (s)
- s->marked = false;
- else {
- r = dns_server_new(m, NULL, DNS_SERVER_SYSTEM, NULL, family, &address);
- if (r < 0)
- goto clear;
- }
- }
-
- LIST_FOREACH_SAFE(servers, s, nx, m->dns_servers)
- if (s->marked) {
- LIST_REMOVE(servers, m->dns_servers, s);
- dns_server_unref(s);
- }
-
- /* Whenever /etc/resolv.conf changes, start using the first
- * DNS server of it. This is useful to deal with broken
- * network managing implementations (like NetworkManager),
- * that when connecting to a VPN place both the VPN DNS
- * servers and the local ones in /etc/resolv.conf. Without
- * resetting the DNS server to use back to the first entry we
- * will continue to use the local one thus being unable to
- * resolve VPN domains. */
- manager_set_dns_server(m, m->dns_servers);
-
- return 0;
-
-clear:
- while (m->dns_servers) {
- s = m->dns_servers;
-
- LIST_REMOVE(servers, m->dns_servers, s);
- dns_server_unref(s);
- }
-
- return r;
-}
-
-static void write_resolv_conf_server(DnsServer *s, FILE *f, unsigned *count) {
- _cleanup_free_ char *t = NULL;
- int r;
-
- assert(s);
- assert(f);
- assert(count);
-
- r = in_addr_to_string(s->family, &s->address, &t);
- if (r < 0) {
- log_warning_errno(r, "Invalid DNS address. Ignoring: %m");
- return;
- }
-
- if (*count == MAXNS)
- fputs("# Too many DNS servers configured, the following entries may be ignored.\n", f);
-
- fprintf(f, "nameserver %s\n", t);
- (*count) ++;
-}
-
-static void write_resolv_conf_search(
- const char *domain, FILE *f,
- unsigned *count,
- unsigned *length) {
-
- assert(domain);
- assert(f);
- assert(length);
-
- if (*count >= MAXDNSRCH ||
- *length + strlen(domain) > 256) {
- if (*count == MAXDNSRCH)
- fputs(" # Too many search domains configured, remaining ones ignored.", f);
- if (*length <= 256)
- fputs(" # Total length of all search domains is too long, remaining ones ignored.", f);
-
- return;
- }
-
- fprintf(f, " %s", domain);
-
- (*length) += strlen(domain);
- (*count) ++;
-}
-
-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"
- "# Third party programs must not access this file directly, but\n"
- "# only through the symlink at /etc/resolv.conf. To manage\n"
- "# resolv.conf(5) in a different way, replace the symlink by a\n"
- "# static file or a different symlink.\n\n", f);
-
- if (ordered_set_isempty(dns))
- fputs("# No DNS servers known.\n", f);
- else {
- DnsServer *s;
- unsigned count = 0;
-
- ORDERED_SET_FOREACH(s, dns, i)
- write_resolv_conf_server(s, f, &count);
- }
-
- if (!ordered_set_isempty(domains)) {
- unsigned length = 0, count = 0;
- char *domain;
-
- fputs("search", f);
- ORDERED_SET_FOREACH(domain, domains, i)
- write_resolv_conf_search(domain, f, &count, &length);
- fputs("\n", f);
- }
-
- 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_ordered_set_free_ OrderedSet *dns = NULL, *domains = NULL;
- DnsServer *s;
- Iterator i;
- Link *l;
- int r;
-
- assert(m);
-
- /* Read the system /etc/resolv.conf first */
- manager_read_resolv_conf(m);
-
- /* Add the full list to a set, to filter out duplicates */
- dns = ordered_set_new(&dns_server_hash_ops);
- if (!dns)
- return -ENOMEM;
-
- 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 = ordered_set_put(dns, s);
- if (r == -EEXIST)
- continue;
- if (r < 0)
- return r;
- }
-
- /* Then, add the per-link servers and domains */
- HASHMAP_FOREACH(l, m->links, i) {
- char **domain;
-
- LIST_FOREACH(servers, s, l->dns_servers) {
- r = ordered_set_put(dns, s);
- if (r == -EEXIST)
- continue;
- if (r < 0)
- return r;
- }
-
- if (!l->unicast_scope)
- continue;
-
- STRV_FOREACH(domain, l->unicast_scope->domains) {
- r = ordered_set_put(domains, *domain);
- if (r == -EEXIST)
- continue;
- if (r < 0)
- return r;
- }
- }
-
- /* If we found nothing, add the fallback servers */
- if (ordered_set_isempty(dns)) {
- LIST_FOREACH(servers, s, m->fallback_dns_servers) {
- r = ordered_set_put(dns, s);
- if (r == -EEXIST)
- continue;
- if (r < 0)
- return r;
- }
- }
-
- r = fopen_temporary_label(path, path, &f, &temp_path);
- if (r < 0)
- return r;
-
- fchmod(fileno(f), 0644);
-
- r = write_resolv_conf_contents(f, dns, domains);
- if (r < 0)
- goto fail;
-
- if (rename(temp_path, path) < 0) {
- r = -errno;
- goto fail;
- }
-
- return 0;
-
-fail:
- (void) unlink(path);
- (void) unlink(temp_path);
- return r;
-}
-
int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) {
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
union {
@@ -1041,7 +782,7 @@ static int write_loop(int fd, void *message, size_t length) {
int manager_write(Manager *m, int fd, DnsPacket *p) {
int r;
- log_debug("Sending %s packet with id %u", DNS_PACKET_QR(p) ? "response" : "query", DNS_PACKET_ID(p));
+ log_debug("Sending %s packet with id %" PRIu16 ".", DNS_PACKET_QR(p) ? "response" : "query", DNS_PACKET_ID(p));
r = write_loop(fd, DNS_PACKET_DATA(p), p->size);
if (r < 0)
@@ -1156,7 +897,7 @@ int manager_send(Manager *m, int fd, int ifindex, int family, const union in_add
assert(port > 0);
assert(p);
- log_debug("Sending %s packet with id %u on interface %i/%s", DNS_PACKET_QR(p) ? "response" : "query", DNS_PACKET_ID(p), ifindex, af_to_name(family));
+ log_debug("Sending %s packet with id %" PRIu16 " on interface %i/%s.", DNS_PACKET_QR(p) ? "response" : "query", DNS_PACKET_ID(p), ifindex, af_to_name(family));
if (family == AF_INET)
return manager_ipv4_send(m, fd, ifindex, &addr->in, port, p);
@@ -1166,97 +907,6 @@ int manager_send(Manager *m, int fd, int ifindex, int family, const union in_add
return -EAFNOSUPPORT;
}
-DnsServer* manager_find_dns_server(Manager *m, int family, const union in_addr_union *in_addr) {
- DnsServer *s;
-
- assert(m);
- assert(in_addr);
-
- LIST_FOREACH(servers, s, m->dns_servers)
- if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0)
- return s;
-
- LIST_FOREACH(servers, s, m->fallback_dns_servers)
- if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0)
- return s;
-
- return NULL;
-}
-
-DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) {
- assert(m);
-
- if (m->current_dns_server == s)
- return s;
-
- if (s) {
- _cleanup_free_ char *ip = NULL;
-
- in_addr_to_string(s->family, &s->address, &ip);
- log_info("Switching to system DNS server %s.", strna(ip));
- }
-
- m->current_dns_server = s;
-
- if (m->unicast_scope)
- dns_cache_flush(&m->unicast_scope->cache);
-
- return s;
-}
-
-DnsServer *manager_get_dns_server(Manager *m) {
- Link *l;
- assert(m);
-
- /* Try to read updates resolv.conf */
- manager_read_resolv_conf(m);
-
- if (!m->current_dns_server)
- manager_set_dns_server(m, m->dns_servers);
-
- if (!m->current_dns_server) {
- bool found = false;
- Iterator i;
-
- /* No DNS servers configured, let's see if there are
- * any on any links. If not, we use the fallback
- * servers */
-
- HASHMAP_FOREACH(l, m->links, i)
- if (l->dns_servers) {
- found = true;
- break;
- }
-
- if (!found)
- manager_set_dns_server(m, m->fallback_dns_servers);
- }
-
- return m->current_dns_server;
-}
-
-void manager_next_dns_server(Manager *m) {
- assert(m);
-
- /* If there's currently no DNS server set, then the next
- * manager_get_dns_server() will find one */
- if (!m->current_dns_server)
- return;
-
- /* Change to the next one */
- if (m->current_dns_server->servers_next) {
- manager_set_dns_server(m, m->current_dns_server->servers_next);
- return;
- }
-
- /* If there was no next one, then start from the beginning of
- * the list */
- if (m->current_dns_server->type == DNS_SERVER_FALLBACK)
- manager_set_dns_server(m, m->fallback_dns_servers);
- else
- manager_set_dns_server(m, m->dns_servers);
-}
-
uint32_t manager_find_mtu(Manager *m) {
uint32_t mtu = 0;
Link *l;
@@ -1391,11 +1041,25 @@ DnsScope* manager_find_scope(Manager *m, DnsPacket *p) {
if (!l)
return NULL;
- if (p->protocol == DNS_PROTOCOL_LLMNR) {
+ switch (p->protocol) {
+ case DNS_PROTOCOL_LLMNR:
if (p->family == AF_INET)
return l->llmnr_ipv4_scope;
else if (p->family == AF_INET6)
return l->llmnr_ipv6_scope;
+
+ break;
+
+ case DNS_PROTOCOL_MDNS:
+ if (p->family == AF_INET)
+ return l->mdns_ipv4_scope;
+ else if (p->family == AF_INET6)
+ return l->mdns_ipv6_scope;
+
+ break;
+
+ default:
+ break;
}
return NULL;
@@ -1410,49 +1074,132 @@ void manager_verify_all(Manager *m) {
dns_zone_verify_all(&s->zone);
}
-void manager_flush_dns_servers(Manager *m, DnsServerType t) {
+int manager_is_own_hostname(Manager *m, const char *name) {
+ int r;
+
+ assert(m);
+ assert(name);
+
+ if (m->llmnr_hostname) {
+ r = dns_name_equal(name, m->llmnr_hostname);
+ if (r != 0)
+ return r;
+ }
+
+ if (m->mdns_hostname)
+ return dns_name_equal(name, m->mdns_hostname);
+
+ return 0;
+}
+
+int manager_compile_dns_servers(Manager *m, OrderedSet **dns) {
DnsServer *s;
+ Iterator i;
+ Link *l;
+ int r;
assert(m);
+ assert(dns);
- if (t == DNS_SERVER_SYSTEM)
- while (m->dns_servers) {
- s = m->dns_servers;
+ r = ordered_set_ensure_allocated(dns, &dns_server_hash_ops);
+ if (r < 0)
+ return r;
- LIST_REMOVE(servers, m->dns_servers, s);
- dns_server_unref(s);
- }
+ /* First add the system-wide servers and domains */
+ LIST_FOREACH(servers, s, m->dns_servers) {
+ r = ordered_set_put(*dns, s);
+ if (r == -EEXIST)
+ continue;
+ if (r < 0)
+ return r;
+ }
- if (t == DNS_SERVER_FALLBACK)
- while (m->fallback_dns_servers) {
- s = m->fallback_dns_servers;
+ /* Then, add the per-link servers */
+ HASHMAP_FOREACH(l, m->links, i) {
+ LIST_FOREACH(servers, s, l->dns_servers) {
+ r = ordered_set_put(*dns, s);
+ if (r == -EEXIST)
+ continue;
+ if (r < 0)
+ return r;
+ }
+ }
- LIST_REMOVE(servers, m->fallback_dns_servers, s);
- dns_server_unref(s);
+ /* If we found nothing, add the fallback servers */
+ if (ordered_set_isempty(*dns)) {
+ LIST_FOREACH(servers, s, m->fallback_dns_servers) {
+ r = ordered_set_put(*dns, s);
+ if (r == -EEXIST)
+ continue;
+ if (r < 0)
+ return r;
}
+ }
+
+ return 0;
}
-int manager_is_own_hostname(Manager *m, const char *name) {
+int manager_compile_search_domains(Manager *m, OrderedSet **domains) {
+ DnsSearchDomain *d;
+ Iterator i;
+ Link *l;
int r;
assert(m);
- assert(name);
+ assert(domains);
- if (m->llmnr_hostname) {
- r = dns_name_equal(name, m->llmnr_hostname);
- if (r != 0)
+ r = ordered_set_ensure_allocated(domains, &dns_name_hash_ops);
+ if (r < 0)
+ return r;
+
+ LIST_FOREACH(domains, d, m->search_domains) {
+ r = ordered_set_put(*domains, d->name);
+ if (r == -EEXIST)
+ continue;
+ if (r < 0)
return r;
}
- if (m->mdns_hostname)
- return dns_name_equal(name, m->mdns_hostname);
+ HASHMAP_FOREACH(l, m->links, i) {
+
+ LIST_FOREACH(domains, d, l->search_domains) {
+ r = ordered_set_put(*domains, d->name);
+ if (r == -EEXIST)
+ continue;
+ if (r < 0)
+ return r;
+ }
+ }
return 0;
}
-static const char* const support_table[_SUPPORT_MAX] = {
- [SUPPORT_NO] = "no",
- [SUPPORT_YES] = "yes",
- [SUPPORT_RESOLVE] = "resolve",
-};
-DEFINE_STRING_TABLE_LOOKUP(support, Support);
+DnssecMode manager_get_dnssec_mode(Manager *m) {
+ assert(m);
+
+ if (m->dnssec_mode != _DNSSEC_MODE_INVALID)
+ return m->dnssec_mode;
+
+ return DNSSEC_NO;
+}
+
+bool manager_dnssec_supported(Manager *m) {
+ DnsServer *server;
+ Iterator i;
+ Link *l;
+
+ assert(m);
+
+ if (manager_get_dnssec_mode(m) == DNSSEC_NO)
+ return false;
+
+ server = manager_get_dns_server(m);
+ if (server && !dns_server_dnssec_supported(server))
+ return false;
+
+ HASHMAP_FOREACH(l, m->links, i)
+ if (!link_dnssec_supported(l))
+ return false;
+
+ return true;
+}
diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h
index fe7fe99505..8b13074298 100644
--- a/src/resolve/resolved-manager.h
+++ b/src/resolve/resolved-manager.h
@@ -22,30 +22,32 @@
***/
#include "sd-event.h"
-#include "sd-network.h"
#include "sd-netlink.h"
-#include "list.h"
+#include "sd-network.h"
+
#include "hashmap.h"
+#include "list.h"
+#include "ordered-set.h"
+#include "resolve-util.h"
typedef struct Manager Manager;
-typedef enum Support Support;
-
-enum Support {
- SUPPORT_NO,
- SUPPORT_YES,
- SUPPORT_RESOLVE,
- _SUPPORT_MAX,
- _SUPPORT_INVALID = -1
-};
#include "resolved-dns-query.h"
+#include "resolved-dns-search-domain.h"
+#include "resolved-dns-server.h"
#include "resolved-dns-stream.h"
+#include "resolved-dns-trust-anchor.h"
#include "resolved-link.h"
+#define MANAGER_SEARCH_DOMAINS_MAX 32
+#define MANAGER_DNS_SERVERS_MAX 32
+
struct Manager {
sd_event *event;
- Support llmnr_support;
+ ResolveSupport llmnr_support;
+ ResolveSupport mdns_support;
+ DnssecMode dnssec_mode;
/* Network */
Hashmap *links;
@@ -67,11 +69,19 @@ struct Manager {
/* Unicast dns */
LIST_HEAD(DnsServer, dns_servers);
LIST_HEAD(DnsServer, fallback_dns_servers);
+ unsigned n_dns_servers; /* counts both main and fallback */
DnsServer *current_dns_server;
- bool read_resolv_conf;
+ LIST_HEAD(DnsSearchDomain, search_domains);
+ unsigned n_search_domains;
+
+ bool need_builtin_fallbacks:1;
+
+ bool read_resolv_conf:1;
usec_t resolv_conf_mtime;
+ DnsTrustAnchor trust_anchor;
+
LIST_HEAD(DnsScope, dns_scopes);
DnsScope *unicast_scope;
@@ -86,6 +96,13 @@ struct Manager {
sd_event_source *llmnr_ipv4_tcp_event_source;
sd_event_source *llmnr_ipv6_tcp_event_source;
+ /* mDNS */
+ int mdns_ipv4_fd;
+ int mdns_ipv6_fd;
+
+ sd_event_source *mdns_ipv4_event_source;
+ sd_event_source *mdns_ipv6_event_source;
+
/* dbus */
sd_bus *bus;
sd_event_source *bus_retry_event_source;
@@ -104,6 +121,9 @@ struct Manager {
sd_bus_slot *prepare_for_sleep_slot;
sd_event_source *sigusr1_event_source;
+
+ unsigned n_transactions_total;
+ unsigned n_dnssec_secure, n_dnssec_insecure, n_dnssec_bogus, n_dnssec_indeterminate;
};
/* Manager */
@@ -112,13 +132,6 @@ int manager_new(Manager **ret);
Manager* manager_free(Manager *m);
int manager_start(Manager *m);
-int manager_read_resolv_conf(Manager *m);
-int manager_write_resolv_conf(Manager *m);
-
-DnsServer *manager_set_dns_server(Manager *m, DnsServer *s);
-DnsServer *manager_find_dns_server(Manager *m, int family, const union in_addr_union *in_addr);
-DnsServer *manager_get_dns_server(Manager *m);
-void manager_next_dns_server(Manager *m);
uint32_t manager_find_mtu(Manager *m);
@@ -137,13 +150,14 @@ DnsScope* manager_find_scope(Manager *m, DnsPacket *p);
void manager_verify_all(Manager *m);
-void manager_flush_dns_servers(Manager *m, DnsServerType t);
-
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
#define EXTRA_CMSG_SPACE 1024
int manager_is_own_hostname(Manager *m, const char *name);
-const char* support_to_string(Support p) _const_;
-int support_from_string(const char *s) _pure_;
+int manager_compile_dns_servers(Manager *m, OrderedSet **servers);
+int manager_compile_search_domains(Manager *m, OrderedSet **domains);
+
+DnssecMode manager_get_dnssec_mode(Manager *m);
+bool manager_dnssec_supported(Manager *m);
diff --git a/src/resolve/resolved-mdns.c b/src/resolve/resolved-mdns.c
new file mode 100644
index 0000000000..d5b253d4f5
--- /dev/null
+++ b/src/resolve/resolved-mdns.c
@@ -0,0 +1,289 @@
+/*-*- 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 <resolv.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "fd-util.h"
+#include "resolved-manager.h"
+#include "resolved-mdns.h"
+
+void manager_mdns_stop(Manager *m) {
+ assert(m);
+
+ m->mdns_ipv4_event_source = sd_event_source_unref(m->mdns_ipv4_event_source);
+ m->mdns_ipv4_fd = safe_close(m->mdns_ipv4_fd);
+
+ m->mdns_ipv6_event_source = sd_event_source_unref(m->mdns_ipv6_event_source);
+ m->mdns_ipv6_fd = safe_close(m->mdns_ipv6_fd);
+}
+
+int manager_mdns_start(Manager *m) {
+ int r;
+
+ assert(m);
+
+ if (m->mdns_support == RESOLVE_SUPPORT_NO)
+ return 0;
+
+ r = manager_mdns_ipv4_fd(m);
+ if (r == -EADDRINUSE)
+ goto eaddrinuse;
+ if (r < 0)
+ return r;
+
+ if (socket_ipv6_is_supported()) {
+ r = manager_mdns_ipv6_fd(m);
+ if (r == -EADDRINUSE)
+ goto eaddrinuse;
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+
+eaddrinuse:
+ log_warning("There appears to be another mDNS responder running. Turning off mDNS support.");
+ m->mdns_support = RESOLVE_SUPPORT_NO;
+ manager_mdns_stop(m);
+
+ return 0;
+}
+
+static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
+ Manager *m = userdata;
+ DnsScope *scope;
+ int r;
+
+ r = manager_recv(m, fd, DNS_PROTOCOL_MDNS, &p);
+ if (r <= 0)
+ return r;
+
+ scope = manager_find_scope(m, p);
+ if (!scope) {
+ log_warning("Got mDNS UDP packet on unknown scope. Ignoring.");
+ return 0;
+ }
+
+ if (dns_packet_validate_reply(p) > 0) {
+ DnsResourceRecord *rr;
+
+ log_debug("Got mDNS reply packet");
+
+ /*
+ * mDNS is different from regular DNS and LLMNR with regard to handling responses.
+ * While on other protocols, we can ignore every answer that doesn't match a question
+ * we broadcast earlier, RFC6762, section 18.1 recommends looking at and caching all
+ * incoming information, regardless of the DNS packet ID.
+ *
+ * Hence, extract the packet here, and try to find a transaction for answer the we got
+ * and complete it. Also store the new information in scope's cache.
+ */
+ r = dns_packet_extract(p);
+ if (r < 0) {
+ log_debug("mDNS packet extraction failed.");
+ return 0;
+ }
+
+ dns_scope_check_conflicts(scope, p);
+
+ DNS_ANSWER_FOREACH(rr, p->answer) {
+ const char *name = DNS_RESOURCE_KEY_NAME(rr->key);
+ DnsTransaction *t;
+
+ /* If the received reply packet contains ANY record that is not .local or .in-addr.arpa,
+ * we assume someone's playing tricks on us and discard the packet completely. */
+ if (!(dns_name_endswith(name, "in-addr.arpa") > 0 ||
+ dns_name_endswith(name, "local") > 0))
+ return 0;
+
+ t = dns_scope_find_transaction(scope, rr->key, false);
+ if (t)
+ dns_transaction_process_reply(t, p);
+ }
+
+ dns_cache_put(&scope->cache, NULL, DNS_PACKET_RCODE(p), p->answer, false, (uint32_t) -1, 0, p->family, &p->sender);
+
+ } else if (dns_packet_validate_query(p) > 0) {
+ log_debug("Got mDNS query packet for id %u", DNS_PACKET_ID(p));
+
+ dns_scope_process_query(scope, NULL, p);
+ } else
+ log_debug("Invalid mDNS UDP packet.");
+
+ return 0;
+}
+
+int manager_mdns_ipv4_fd(Manager *m) {
+ union sockaddr_union sa = {
+ .in.sin_family = AF_INET,
+ .in.sin_port = htobe16(MDNS_PORT),
+ };
+ static const int one = 1, pmtu = IP_PMTUDISC_DONT, ttl = 255;
+ int r;
+
+ assert(m);
+
+ if (m->mdns_ipv4_fd >= 0)
+ return m->mdns_ipv4_fd;
+
+ m->mdns_ipv4_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (m->mdns_ipv4_fd < 0)
+ return -errno;
+
+ r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
+ if (r < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
+ if (r < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one));
+ if (r < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ r = setsockopt(m->mdns_ipv4_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+ if (r < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
+ if (r < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one));
+ if (r < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ /* Disable Don't-Fragment bit in the IP header */
+ r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu));
+ if (r < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ r = bind(m->mdns_ipv4_fd, &sa.sa, sizeof(sa.in));
+ if (r < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ r = sd_event_add_io(m->event, &m->mdns_ipv4_event_source, m->mdns_ipv4_fd, EPOLLIN, on_mdns_packet, m);
+ if (r < 0)
+ goto fail;
+
+ return m->mdns_ipv4_fd;
+
+fail:
+ m->mdns_ipv4_fd = safe_close(m->mdns_ipv4_fd);
+ return r;
+}
+
+int manager_mdns_ipv6_fd(Manager *m) {
+ union sockaddr_union sa = {
+ .in6.sin6_family = AF_INET6,
+ .in6.sin6_port = htobe16(MDNS_PORT),
+ };
+ static const int one = 1, ttl = 255;
+ int r;
+
+ assert(m);
+
+ if (m->mdns_ipv6_fd >= 0)
+ return m->mdns_ipv6_fd;
+
+ m->mdns_ipv6_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (m->mdns_ipv6_fd < 0)
+ return -errno;
+
+ r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl));
+ if (r < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ /* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */
+ r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl));
+ if (r < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &one, sizeof(one));
+ if (r < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
+ if (r < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ r = setsockopt(m->mdns_ipv6_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+ if (r < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
+ if (r < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one));
+ if (r < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ r = bind(m->mdns_ipv6_fd, &sa.sa, sizeof(sa.in6));
+ if (r < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ r = sd_event_add_io(m->event, &m->mdns_ipv6_event_source, m->mdns_ipv6_fd, EPOLLIN, on_mdns_packet, m);
+ if (r < 0)
+ goto fail;
+
+ return m->mdns_ipv6_fd;
+
+fail:
+ m->mdns_ipv6_fd = safe_close(m->mdns_ipv6_fd);
+ return r;
+}
diff --git a/src/resolve/resolved-mdns.h b/src/resolve/resolved-mdns.h
new file mode 100644
index 0000000000..8a84010615
--- /dev/null
+++ b/src/resolve/resolved-mdns.h
@@ -0,0 +1,32 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ 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 "resolved-manager.h"
+
+#define MDNS_PORT 5353
+
+int manager_mdns_ipv4_fd(Manager *m);
+int manager_mdns_ipv6_fd(Manager *m);
+
+void manager_mdns_stop(Manager *m);
+int manager_mdns_start(Manager *m);
diff --git a/src/resolve/resolved-resolv-conf.c b/src/resolve/resolved-resolv-conf.c
new file mode 100644
index 0000000000..7567f4c369
--- /dev/null
+++ b/src/resolve/resolved-resolv-conf.c
@@ -0,0 +1,271 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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/>.
+ ***/
+
+#include <resolv.h>
+
+#include "alloc-util.h"
+#include "dns-domain.h"
+#include "fd-util.h"
+#include "fileio-label.h"
+#include "fileio.h"
+#include "ordered-set.h"
+#include "resolved-conf.h"
+#include "resolved-resolv-conf.h"
+#include "string-util.h"
+#include "strv.h"
+
+int manager_read_resolv_conf(Manager *m) {
+ _cleanup_fclose_ FILE *f = NULL;
+ struct stat st, own;
+ char line[LINE_MAX];
+ usec_t t;
+ int r;
+
+ assert(m);
+
+ /* Reads the system /etc/resolv.conf, if it exists and is not
+ * symlinked to our own resolv.conf instance */
+
+ if (!m->read_resolv_conf)
+ return 0;
+
+ r = stat("/etc/resolv.conf", &st);
+ if (r < 0) {
+ if (errno == ENOENT)
+ return 0;
+
+ r = log_warning_errno(errno, "Failed to stat /etc/resolv.conf: %m");
+ goto clear;
+ }
+
+ /* Have we already seen the file? */
+ t = timespec_load(&st.st_mtim);
+ if (t == m->resolv_conf_mtime)
+ return 0;
+
+ /* Is it symlinked to our own file? */
+ if (stat("/run/systemd/resolve/resolv.conf", &own) >= 0 &&
+ st.st_dev == own.st_dev &&
+ st.st_ino == own.st_ino)
+ return 0;
+
+ f = fopen("/etc/resolv.conf", "re");
+ if (!f) {
+ if (errno == ENOENT)
+ return 0;
+
+ r = log_warning_errno(errno, "Failed to open /etc/resolv.conf: %m");
+ goto clear;
+ }
+
+ if (fstat(fileno(f), &st) < 0) {
+ r = log_error_errno(errno, "Failed to stat open file: %m");
+ goto clear;
+ }
+
+ dns_server_mark_all(m->dns_servers);
+ dns_search_domain_mark_all(m->search_domains);
+
+ FOREACH_LINE(line, f, r = -errno; goto clear) {
+ const char *a;
+ char *l;
+
+ l = strstrip(line);
+ if (*l == '#' || *l == ';')
+ continue;
+
+ a = first_word(l, "nameserver");
+ if (a) {
+ r = manager_add_dns_server_by_string(m, DNS_SERVER_SYSTEM, a);
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse DNS server address '%s', ignoring.", a);
+
+ continue;
+ }
+
+ a = first_word(l, "domain");
+ if (!a) /* We treat "domain" lines, and "search" lines as equivalent, and add both to our list. */
+ a = first_word(l, "search");
+ if (a) {
+ r = manager_parse_search_domains_and_warn(m, a);
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse search domain string '%s', ignoring.", a);
+ }
+ }
+
+ m->resolv_conf_mtime = t;
+
+ /* Flush out all servers and search domains that are still
+ * marked. Those are then ones that didn't appear in the new
+ * /etc/resolv.conf */
+ dns_server_unlink_marked(m->dns_servers);
+ dns_search_domain_unlink_marked(m->search_domains);
+
+ /* Whenever /etc/resolv.conf changes, start using the first
+ * DNS server of it. This is useful to deal with broken
+ * network managing implementations (like NetworkManager),
+ * that when connecting to a VPN place both the VPN DNS
+ * servers and the local ones in /etc/resolv.conf. Without
+ * resetting the DNS server to use back to the first entry we
+ * will continue to use the local one thus being unable to
+ * resolve VPN domains. */
+ manager_set_dns_server(m, m->dns_servers);
+
+ /* Unconditionally flush the cache when /etc/resolv.conf is
+ * modified, even if the data it contained was completely
+ * identical to the previous version we used. We do this
+ * because altering /etc/resolv.conf is typically done when
+ * the network configuration changes, and that should be
+ * enough to flush the global unicast DNS cache. */
+ if (m->unicast_scope)
+ dns_cache_flush(&m->unicast_scope->cache);
+
+ return 0;
+
+clear:
+ dns_server_unlink_all(m->dns_servers);
+ dns_search_domain_unlink_all(m->search_domains);
+ return r;
+}
+
+static void write_resolv_conf_server(DnsServer *s, FILE *f, unsigned *count) {
+ assert(s);
+ assert(f);
+ assert(count);
+
+ (void) dns_server_string(s);
+
+ if (!s->server_string) {
+ log_warning("Our of memory, or invalid DNS address. Ignoring server.");
+ return;
+ }
+
+ if (*count == MAXNS)
+ fputs("# Too many DNS servers configured, the following entries may be ignored.\n", f);
+ (*count) ++;
+
+ fprintf(f, "nameserver %s\n", s->server_string);
+}
+
+static void write_resolv_conf_search(
+ const char *domain,
+ FILE *f,
+ unsigned *count,
+ unsigned *length) {
+
+ assert(domain);
+ assert(f);
+ assert(length);
+
+ if (*count >= MAXDNSRCH ||
+ *length + strlen(domain) > 256) {
+ if (*count == MAXDNSRCH)
+ fputs(" # Too many search domains configured, remaining ones ignored.", f);
+ if (*length <= 256)
+ fputs(" # Total length of all search domains is too long, remaining ones ignored.", f);
+
+ return;
+ }
+
+ (*length) += strlen(domain);
+ (*count) ++;
+
+ fputc(' ', f);
+ fputs(domain, f);
+}
+
+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"
+ "# Third party programs must not access this file directly, but\n"
+ "# only through the symlink at /etc/resolv.conf. To manage\n"
+ "# resolv.conf(5) in a different way, replace the symlink by a\n"
+ "# static file or a different symlink.\n\n", f);
+
+ if (ordered_set_isempty(dns))
+ fputs("# No DNS servers known.\n", f);
+ else {
+ unsigned count = 0;
+ DnsServer *s;
+
+ ORDERED_SET_FOREACH(s, dns, i)
+ write_resolv_conf_server(s, f, &count);
+ }
+
+ if (!ordered_set_isempty(domains)) {
+ unsigned length = 0, count = 0;
+ char *domain;
+
+ fputs("search", f);
+ ORDERED_SET_FOREACH(domain, domains, i)
+ write_resolv_conf_search(domain, f, &count, &length);
+ fputs("\n", f);
+ }
+
+ return fflush_and_check(f);
+}
+
+int manager_write_resolv_conf(Manager *m) {
+
+ #define PRIVATE_RESOLV_CONF "/run/systemd/resolve/resolv.conf"
+
+ _cleanup_ordered_set_free_ OrderedSet *dns = NULL, *domains = NULL;
+ _cleanup_free_ char *temp_path = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ int r;
+
+ assert(m);
+
+ /* Read the system /etc/resolv.conf first */
+ manager_read_resolv_conf(m);
+
+ /* Add the full list to a set, to filter out duplicates */
+ r = manager_compile_dns_servers(m, &dns);
+ if (r < 0)
+ return r;
+
+ r = manager_compile_search_domains(m, &domains);
+ if (r < 0)
+ return r;
+
+ r = fopen_temporary_label(PRIVATE_RESOLV_CONF, PRIVATE_RESOLV_CONF, &f, &temp_path);
+ if (r < 0)
+ return r;
+
+ fchmod(fileno(f), 0644);
+
+ r = write_resolv_conf_contents(f, dns, domains);
+ if (r < 0)
+ goto fail;
+
+ if (rename(temp_path, PRIVATE_RESOLV_CONF) < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ (void) unlink(PRIVATE_RESOLV_CONF);
+ (void) unlink(temp_path);
+ return r;
+}
diff --git a/src/libsystemd-network/lldp-util.h b/src/resolve/resolved-resolv-conf.h
index 112001e4b9..a3355e994b 100644
--- a/src/libsystemd-network/lldp-util.h
+++ b/src/resolve/resolved-resolv-conf.h
@@ -1,10 +1,11 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+#pragma once
+
/***
This file is part of systemd.
- Copyright (C) 2014 Tom Gundersen
- Copyright (C) 2014 Susant Sahani
+ 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
@@ -20,7 +21,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#pragma once
+#include "resolved-manager.h"
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_lldp *, sd_lldp_free);
-#define _cleanup_lldp_free_ _cleanup_(sd_lldp_freep)
+int manager_read_resolv_conf(Manager *m);
+int manager_write_resolv_conf(Manager *m);
diff --git a/src/resolve/resolved.c b/src/resolve/resolved.c
index 32e61af925..472bb32764 100644
--- a/src/resolve/resolved.c
+++ b/src/resolve/resolved.c
@@ -19,15 +19,17 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "sd-event.h"
#include "sd-daemon.h"
+#include "sd-event.h"
+
+#include "capability-util.h"
#include "mkdir.h"
-#include "capability.h"
+#include "resolved-conf.h"
+#include "resolved-manager.h"
+#include "resolved-resolv-conf.h"
#include "selinux-util.h"
#include "signal-util.h"
-
-#include "resolved-manager.h"
-#include "resolved-conf.h"
+#include "user-util.h"
int main(int argc, char *argv[]) {
_cleanup_(manager_freep) Manager *m = NULL;
@@ -79,10 +81,6 @@ int main(int argc, char *argv[]) {
goto finish;
}
- r = manager_parse_config_file(m);
- if (r < 0)
- log_warning_errno(r, "Failed to parse configuration file: %m");
-
r = manager_start(m);
if (r < 0) {
log_error_errno(r, "Failed to start manager: %m");
diff --git a/src/resolve/resolved.conf.in b/src/resolve/resolved.conf.in
index 3eb19e42b7..efc9c6733a 100644
--- a/src/resolve/resolved.conf.in
+++ b/src/resolve/resolved.conf.in
@@ -14,4 +14,6 @@
[Resolve]
#DNS=
#FallbackDNS=@DNS_SERVERS@
+#Domains=
#LLMNR=yes
+#DNSSEC=no
diff --git a/src/resolve/test-dnssec-complex.c b/src/resolve/test-dnssec-complex.c
new file mode 100644
index 0000000000..caac251e83
--- /dev/null
+++ b/src/resolve/test-dnssec-complex.c
@@ -0,0 +1,238 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2016 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 <netinet/ip.h>
+
+#include "sd-bus.h"
+
+#include "af-list.h"
+#include "alloc-util.h"
+#include "bus-common-errors.h"
+#include "dns-type.h"
+#include "random-util.h"
+#include "string-util.h"
+#include "time-util.h"
+
+#define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
+
+static void prefix_random(const char *name, char **ret) {
+ uint64_t i, u;
+ char *m = NULL;
+
+ u = 1 + (random_u64() & 3);
+
+ for (i = 0; i < u; i++) {
+ _cleanup_free_ char *b = NULL;
+ char *x;
+
+ assert_se(asprintf(&b, "x%" PRIu64 "x", random_u64()));
+ x = strjoin(b, ".", name, NULL);
+ assert_se(x);
+
+ free(m);
+ m = x;
+ }
+
+ *ret = m;
+ }
+
+static void test_rr_lookup(sd_bus *bus, const char *name, uint16_t type, const char *result) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_free_ char *m = NULL;
+ int r;
+
+ /* If the name starts with a dot, we prefix one to three random labels */
+ if (startswith(name, ".")) {
+ prefix_random(name + 1, &m);
+ name = m;
+ }
+
+ assert_se(sd_bus_message_new_method_call(
+ bus,
+ &req,
+ "org.freedesktop.resolve1",
+ "/org/freedesktop/resolve1",
+ "org.freedesktop.resolve1.Manager",
+ "ResolveRecord") >= 0);
+
+ assert_se(sd_bus_message_append(req, "isqqt", 0, name, DNS_CLASS_IN, type, UINT64_C(0)) >= 0);
+
+ r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
+
+ if (r < 0) {
+ assert_se(result);
+ assert_se(sd_bus_error_has_name(&error, result));
+ log_info("[OK] %s/%s resulted in <%s>.", name, dns_type_to_string(type), error.name);
+ } else {
+ assert_se(!result);
+ log_info("[OK] %s/%s succeeded.", name, dns_type_to_string(type));
+ }
+}
+
+static void test_hostname_lookup(sd_bus *bus, const char *name, int family, const char *result) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_free_ char *m = NULL;
+ const char *af;
+ int r;
+
+ af = family == AF_UNSPEC ? "AF_UNSPEC" : af_to_name(family);
+
+ /* If the name starts with a dot, we prefix one to three random labels */
+ if (startswith(name, ".")) {
+ prefix_random(name + 1, &m);
+ name = m;
+ }
+
+ assert_se(sd_bus_message_new_method_call(
+ bus,
+ &req,
+ "org.freedesktop.resolve1",
+ "/org/freedesktop/resolve1",
+ "org.freedesktop.resolve1.Manager",
+ "ResolveHostname") >= 0);
+
+ assert_se(sd_bus_message_append(req, "isit", 0, name, family, UINT64_C(0)) >= 0);
+
+ r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
+
+ if (r < 0) {
+ assert_se(result);
+ assert_se(sd_bus_error_has_name(&error, result));
+ log_info("[OK] %s/%s resulted in <%s>.", name, af, error.name);
+ } else {
+ assert_se(!result);
+ log_info("[OK] %s/%s succeeded.", name, af);
+ }
+
+}
+
+int main(int argc, char* argv[]) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+
+ /* Note that this is a manual test as it requires:
+ *
+ * Full network access
+ * A DNSSEC capable DNS server
+ * That zones contacted are still set up as they were when I wrote this.
+ */
+
+ assert_se(sd_bus_open_system(&bus) >= 0);
+
+ /* Normally signed */
+ test_rr_lookup(bus, "www.eurid.eu", DNS_TYPE_A, NULL);
+ test_hostname_lookup(bus, "www.eurid.eu", AF_UNSPEC, NULL);
+
+ test_rr_lookup(bus, "sigok.verteiltesysteme.net", DNS_TYPE_A, NULL);
+ test_hostname_lookup(bus, "sigok.verteiltesysteme.net", AF_UNSPEC, NULL);
+
+ /* Normally signed, NODATA */
+ test_rr_lookup(bus, "www.eurid.eu", DNS_TYPE_RP, BUS_ERROR_NO_SUCH_RR);
+ test_rr_lookup(bus, "sigok.verteiltesysteme.net", DNS_TYPE_RP, BUS_ERROR_NO_SUCH_RR);
+
+ /* Invalid signature */
+ test_rr_lookup(bus, "sigfail.verteiltesysteme.net", DNS_TYPE_A, BUS_ERROR_DNSSEC_FAILED);
+ test_hostname_lookup(bus, "sigfail.verteiltesysteme.net", AF_INET, BUS_ERROR_DNSSEC_FAILED);
+
+ /* Invalid signature, RSA, wildcard */
+ test_rr_lookup(bus, ".wilda.rhybar.0skar.cz", DNS_TYPE_A, BUS_ERROR_DNSSEC_FAILED);
+ test_hostname_lookup(bus, ".wilda.rhybar.0skar.cz", AF_INET, BUS_ERROR_DNSSEC_FAILED);
+
+ /* Invalid signature, ECDSA, wildcard */
+ test_rr_lookup(bus, ".wilda.rhybar.ecdsa.0skar.cz", DNS_TYPE_A, BUS_ERROR_DNSSEC_FAILED);
+ test_hostname_lookup(bus, ".wilda.rhybar.ecdsa.0skar.cz", AF_INET, BUS_ERROR_DNSSEC_FAILED);
+
+ /* NXDOMAIN in NSEC domain */
+ test_rr_lookup(bus, "hhh.nasa.gov", DNS_TYPE_A, _BUS_ERROR_DNS "NXDOMAIN");
+ test_hostname_lookup(bus, "hhh.nasa.gov", AF_UNSPEC, _BUS_ERROR_DNS "NXDOMAIN");
+
+ /* wildcard, NSEC zone */
+ test_rr_lookup(bus, ".wilda.nsec.0skar.cz", DNS_TYPE_A, NULL);
+ test_hostname_lookup(bus, ".wilda.nsec.0skar.cz", AF_INET, NULL);
+
+ /* wildcard, NSEC zone, NODATA */
+ test_rr_lookup(bus, ".wilda.nsec.0skar.cz", DNS_TYPE_RP, BUS_ERROR_NO_SUCH_RR);
+
+ /* wildcard, NSEC3 zone */
+ test_rr_lookup(bus, ".wilda.0skar.cz", DNS_TYPE_A, NULL);
+ test_hostname_lookup(bus, ".wilda.0skar.cz", AF_INET, NULL);
+
+ /* wildcard, NSEC3 zone, NODATA */
+ test_rr_lookup(bus, ".wilda.0skar.cz", DNS_TYPE_RP, BUS_ERROR_NO_SUCH_RR);
+
+ /* wildcard, NSEC zone, CNAME */
+ test_rr_lookup(bus, ".wild.nsec.0skar.cz", DNS_TYPE_A, NULL);
+ test_hostname_lookup(bus, ".wild.nsec.0skar.cz", AF_UNSPEC, NULL);
+ test_hostname_lookup(bus, ".wild.nsec.0skar.cz", AF_INET, NULL);
+
+ /* wildcard, NSEC zone, NODATA, CNAME */
+ test_rr_lookup(bus, ".wild.nsec.0skar.cz", DNS_TYPE_RP, BUS_ERROR_NO_SUCH_RR);
+
+ /* wildcard, NSEC3 zone, CNAME */
+ test_rr_lookup(bus, ".wild.0skar.cz", DNS_TYPE_A, NULL);
+ test_hostname_lookup(bus, ".wild.0skar.cz", AF_UNSPEC, NULL);
+ test_hostname_lookup(bus, ".wild.0skar.cz", AF_INET, NULL);
+
+ /* wildcard, NSEC3 zone, NODATA, CNAME */
+ test_rr_lookup(bus, ".wild.0skar.cz", DNS_TYPE_RP, BUS_ERROR_NO_SUCH_RR);
+
+ /* NODATA due to empty non-terminal in NSEC domain */
+ test_rr_lookup(bus, "herndon.nasa.gov", DNS_TYPE_A, BUS_ERROR_NO_SUCH_RR);
+ test_hostname_lookup(bus, "herndon.nasa.gov", AF_UNSPEC, BUS_ERROR_NO_SUCH_RR);
+ test_hostname_lookup(bus, "herndon.nasa.gov", AF_INET, BUS_ERROR_NO_SUCH_RR);
+ test_hostname_lookup(bus, "herndon.nasa.gov", AF_INET6, BUS_ERROR_NO_SUCH_RR);
+
+ /* NXDOMAIN in NSEC root zone: */
+ test_rr_lookup(bus, "jasdhjas.kjkfgjhfjg", DNS_TYPE_A, _BUS_ERROR_DNS "NXDOMAIN");
+ test_hostname_lookup(bus, "jasdhjas.kjkfgjhfjg", AF_UNSPEC, _BUS_ERROR_DNS "NXDOMAIN");
+ test_hostname_lookup(bus, "jasdhjas.kjkfgjhfjg", AF_INET, _BUS_ERROR_DNS "NXDOMAIN");
+ test_hostname_lookup(bus, "jasdhjas.kjkfgjhfjg", AF_INET6, _BUS_ERROR_DNS "NXDOMAIN");
+
+ /* NXDOMAIN in NSEC3 .com zone: */
+ test_rr_lookup(bus, "kjkfgjhfjgsdfdsfd.com", DNS_TYPE_A, _BUS_ERROR_DNS "NXDOMAIN");
+ test_hostname_lookup(bus, "kjkfgjhfjgsdfdsfd.com", AF_INET, _BUS_ERROR_DNS "NXDOMAIN");
+ test_hostname_lookup(bus, "kjkfgjhfjgsdfdsfd.com", AF_INET6, _BUS_ERROR_DNS "NXDOMAIN");
+ test_hostname_lookup(bus, "kjkfgjhfjgsdfdsfd.com", AF_UNSPEC, _BUS_ERROR_DNS "NXDOMAIN");
+
+ /* Unsigned A */
+ test_rr_lookup(bus, "poettering.de", DNS_TYPE_A, NULL);
+ test_rr_lookup(bus, "poettering.de", DNS_TYPE_AAAA, NULL);
+ test_hostname_lookup(bus, "poettering.de", AF_UNSPEC, NULL);
+ test_hostname_lookup(bus, "poettering.de", AF_INET, NULL);
+ test_hostname_lookup(bus, "poettering.de", AF_INET6, NULL);
+
+#if HAVE_LIBIDN
+ /* Unsigned A with IDNA conversion necessary */
+ test_hostname_lookup(bus, "pöttering.de", AF_UNSPEC, NULL);
+ test_hostname_lookup(bus, "pöttering.de", AF_INET, NULL);
+ test_hostname_lookup(bus, "pöttering.de", AF_INET6, NULL);
+#endif
+
+ /* DNAME, pointing to NXDOMAIN */
+ test_rr_lookup(bus, ".ireallyhpoethisdoesnexist.xn--kprw13d.", DNS_TYPE_A, _BUS_ERROR_DNS "NXDOMAIN");
+ test_rr_lookup(bus, ".ireallyhpoethisdoesnexist.xn--kprw13d.", DNS_TYPE_RP, _BUS_ERROR_DNS "NXDOMAIN");
+ test_hostname_lookup(bus, ".ireallyhpoethisdoesntexist.xn--kprw13d.", AF_UNSPEC, _BUS_ERROR_DNS "NXDOMAIN");
+ test_hostname_lookup(bus, ".ireallyhpoethisdoesntexist.xn--kprw13d.", AF_INET, _BUS_ERROR_DNS "NXDOMAIN");
+ test_hostname_lookup(bus, ".ireallyhpoethisdoesntexist.xn--kprw13d.", AF_INET6, _BUS_ERROR_DNS "NXDOMAIN");
+
+ return 0;
+}
diff --git a/src/resolve/test-dnssec.c b/src/resolve/test-dnssec.c
new file mode 100644
index 0000000000..45fe1997e2
--- /dev/null
+++ b/src/resolve/test-dnssec.c
@@ -0,0 +1,338 @@
+/*-*- 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 <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+#include "alloc-util.h"
+#include "resolved-dns-dnssec.h"
+#include "resolved-dns-rr.h"
+#include "string-util.h"
+#include "hexdecoct.h"
+
+static void test_dnssec_verify_rrset2(void) {
+
+ static const uint8_t signature_blob[] = {
+ 0x48, 0x45, 0xc8, 0x8b, 0xc0, 0x14, 0x92, 0xf5, 0x15, 0xc6, 0x84, 0x9d, 0x2f, 0xe3, 0x32, 0x11,
+ 0x7d, 0xf1, 0xe6, 0x87, 0xb9, 0x42, 0xd3, 0x8b, 0x9e, 0xaf, 0x92, 0x31, 0x0a, 0x53, 0xad, 0x8b,
+ 0xa7, 0x5c, 0x83, 0x39, 0x8c, 0x28, 0xac, 0xce, 0x6e, 0x9c, 0x18, 0xe3, 0x31, 0x16, 0x6e, 0xca,
+ 0x38, 0x31, 0xaf, 0xd9, 0x94, 0xf1, 0x84, 0xb1, 0xdf, 0x5a, 0xc2, 0x73, 0x22, 0xf6, 0xcb, 0xa2,
+ 0xe7, 0x8c, 0x77, 0x0c, 0x74, 0x2f, 0xc2, 0x13, 0xb0, 0x93, 0x51, 0xa9, 0x4f, 0xae, 0x0a, 0xda,
+ 0x45, 0xcc, 0xfd, 0x43, 0x99, 0x36, 0x9a, 0x0d, 0x21, 0xe0, 0xeb, 0x30, 0x65, 0xd4, 0xa0, 0x27,
+ 0x37, 0x3b, 0xe4, 0xc1, 0xc5, 0xa1, 0x2a, 0xd1, 0x76, 0xc4, 0x7e, 0x64, 0x0e, 0x5a, 0xa6, 0x50,
+ 0x24, 0xd5, 0x2c, 0xcc, 0x6d, 0xe5, 0x37, 0xea, 0xbd, 0x09, 0x34, 0xed, 0x24, 0x06, 0xa1, 0x22,
+ };
+
+ static const uint8_t dnskey_blob[] = {
+ 0x03, 0x01, 0x00, 0x01, 0xc3, 0x7f, 0x1d, 0xd1, 0x1c, 0x97, 0xb1, 0x13, 0x34, 0x3a, 0x9a, 0xea,
+ 0xee, 0xd9, 0x5a, 0x11, 0x1b, 0x17, 0xc7, 0xe3, 0xd4, 0xda, 0x20, 0xbc, 0x5d, 0xba, 0x74, 0xe3,
+ 0x37, 0x99, 0xec, 0x25, 0xce, 0x93, 0x7f, 0xbd, 0x22, 0x73, 0x7e, 0x14, 0x71, 0xe0, 0x60, 0x07,
+ 0xd4, 0x39, 0x8b, 0x5e, 0xe9, 0xba, 0x25, 0xe8, 0x49, 0xe9, 0x34, 0xef, 0xfe, 0x04, 0x5c, 0xa5,
+ 0x27, 0xcd, 0xa9, 0xda, 0x70, 0x05, 0x21, 0xab, 0x15, 0x82, 0x24, 0xc3, 0x94, 0xf5, 0xd7, 0xb7,
+ 0xc4, 0x66, 0xcb, 0x32, 0x6e, 0x60, 0x2b, 0x55, 0x59, 0x28, 0x89, 0x8a, 0x72, 0xde, 0x88, 0x56,
+ 0x27, 0x95, 0xd9, 0xac, 0x88, 0x4f, 0x65, 0x2b, 0x68, 0xfc, 0xe6, 0x41, 0xc1, 0x1b, 0xef, 0x4e,
+ 0xd6, 0xc2, 0x0f, 0x64, 0x88, 0x95, 0x5e, 0xdd, 0x3a, 0x02, 0x07, 0x50, 0xa9, 0xda, 0xa4, 0x49,
+ 0x74, 0x62, 0xfe, 0xd7,
+ };
+
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *nsec = NULL, *rrsig = NULL, *dnskey = NULL;
+ _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
+ DnssecResult result;
+
+ nsec = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_NSEC, "nasa.gov");
+ assert_se(nsec);
+
+ nsec->nsec.next_domain_name = strdup("3D-Printing.nasa.gov");
+ assert_se(nsec->nsec.next_domain_name);
+
+ nsec->nsec.types = bitmap_new();
+ assert_se(nsec->nsec.types);
+ assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_A) >= 0);
+ assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_NS) >= 0);
+ assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_SOA) >= 0);
+ assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_MX) >= 0);
+ assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_TXT) >= 0);
+ assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_RRSIG) >= 0);
+ assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_NSEC) >= 0);
+ assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_DNSKEY) >= 0);
+ assert_se(bitmap_set(nsec->nsec.types, 65534) >= 0);
+
+ log_info("NSEC: %s", strna(dns_resource_record_to_string(nsec)));
+
+ rrsig = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_RRSIG, "NaSa.GOV.");
+ assert_se(rrsig);
+
+ rrsig->rrsig.type_covered = DNS_TYPE_NSEC;
+ rrsig->rrsig.algorithm = DNSSEC_ALGORITHM_RSASHA256;
+ rrsig->rrsig.labels = 2;
+ rrsig->rrsig.original_ttl = 300;
+ rrsig->rrsig.expiration = 0x5689002f;
+ rrsig->rrsig.inception = 0x56617230;
+ rrsig->rrsig.key_tag = 30390;
+ rrsig->rrsig.signer = strdup("Nasa.Gov.");
+ assert_se(rrsig->rrsig.signer);
+ rrsig->rrsig.signature_size = sizeof(signature_blob);
+ rrsig->rrsig.signature = memdup(signature_blob, rrsig->rrsig.signature_size);
+ assert_se(rrsig->rrsig.signature);
+
+ log_info("RRSIG: %s", strna(dns_resource_record_to_string(rrsig)));
+
+ dnskey = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, "nASA.gOV");
+ assert_se(dnskey);
+
+ dnskey->dnskey.flags = 256;
+ dnskey->dnskey.protocol = 3;
+ dnskey->dnskey.algorithm = DNSSEC_ALGORITHM_RSASHA256;
+ dnskey->dnskey.key_size = sizeof(dnskey_blob);
+ dnskey->dnskey.key = memdup(dnskey_blob, sizeof(dnskey_blob));
+ assert_se(dnskey->dnskey.key);
+
+ log_info("DNSKEY: %s", strna(dns_resource_record_to_string(dnskey)));
+ log_info("DNSKEY keytag: %u", dnssec_keytag(dnskey, false));
+
+ assert_se(dnssec_key_match_rrsig(nsec->key, rrsig) > 0);
+ assert_se(dnssec_rrsig_match_dnskey(rrsig, dnskey, false) > 0);
+
+ answer = dns_answer_new(1);
+ assert_se(answer);
+ assert_se(dns_answer_add(answer, nsec, 0, DNS_ANSWER_AUTHENTICATED) >= 0);
+
+ /* Validate the RR as it if was 2015-12-11 today */
+ assert_se(dnssec_verify_rrset(answer, nsec->key, rrsig, dnskey, 1449849318*USEC_PER_SEC, &result) >= 0);
+ assert_se(result == DNSSEC_VALIDATED);
+}
+
+static void test_dnssec_verify_rrset(void) {
+
+ static const uint8_t signature_blob[] = {
+ 0x7f, 0x79, 0xdd, 0x5e, 0x89, 0x79, 0x18, 0xd0, 0x34, 0x86, 0x8c, 0x72, 0x77, 0x75, 0x48, 0x4d,
+ 0xc3, 0x7d, 0x38, 0x04, 0xab, 0xcd, 0x9e, 0x4c, 0x82, 0xb0, 0x92, 0xca, 0xe9, 0x66, 0xe9, 0x6e,
+ 0x47, 0xc7, 0x68, 0x8c, 0x94, 0xf6, 0x69, 0xcb, 0x75, 0x94, 0xe6, 0x30, 0xa6, 0xfb, 0x68, 0x64,
+ 0x96, 0x1a, 0x84, 0xe1, 0xdc, 0x16, 0x4c, 0x83, 0x6c, 0x44, 0xf2, 0x74, 0x4d, 0x74, 0x79, 0x8f,
+ 0xf3, 0xf4, 0x63, 0x0d, 0xef, 0x5a, 0xe7, 0xe2, 0xfd, 0xf2, 0x2b, 0x38, 0x7c, 0x28, 0x96, 0x9d,
+ 0xb6, 0xcd, 0x5c, 0x3b, 0x57, 0xe2, 0x24, 0x78, 0x65, 0xd0, 0x9e, 0x77, 0x83, 0x09, 0x6c, 0xff,
+ 0x3d, 0x52, 0x3f, 0x6e, 0xd1, 0xed, 0x2e, 0xf9, 0xee, 0x8e, 0xa6, 0xbe, 0x9a, 0xa8, 0x87, 0x76,
+ 0xd8, 0x77, 0xcc, 0x96, 0xa0, 0x98, 0xa1, 0xd1, 0x68, 0x09, 0x43, 0xcf, 0x56, 0xd9, 0xd1, 0x66,
+ };
+
+ static const uint8_t dnskey_blob[] = {
+ 0x03, 0x01, 0x00, 0x01, 0x9b, 0x49, 0x9b, 0xc1, 0xf9, 0x9a, 0xe0, 0x4e, 0xcf, 0xcb, 0x14, 0x45,
+ 0x2e, 0xc9, 0xf9, 0x74, 0xa7, 0x18, 0xb5, 0xf3, 0xde, 0x39, 0x49, 0xdf, 0x63, 0x33, 0x97, 0x52,
+ 0xe0, 0x8e, 0xac, 0x50, 0x30, 0x8e, 0x09, 0xd5, 0x24, 0x3d, 0x26, 0xa4, 0x49, 0x37, 0x2b, 0xb0,
+ 0x6b, 0x1b, 0xdf, 0xde, 0x85, 0x83, 0xcb, 0x22, 0x4e, 0x60, 0x0a, 0x91, 0x1a, 0x1f, 0xc5, 0x40,
+ 0xb1, 0xc3, 0x15, 0xc1, 0x54, 0x77, 0x86, 0x65, 0x53, 0xec, 0x10, 0x90, 0x0c, 0x91, 0x00, 0x5e,
+ 0x15, 0xdc, 0x08, 0x02, 0x4c, 0x8c, 0x0d, 0xc0, 0xac, 0x6e, 0xc4, 0x3e, 0x1b, 0x80, 0x19, 0xe4,
+ 0xf7, 0x5f, 0x77, 0x51, 0x06, 0x87, 0x61, 0xde, 0xa2, 0x18, 0x0f, 0x40, 0x8b, 0x79, 0x72, 0xfa,
+ 0x8d, 0x1a, 0x44, 0x47, 0x0d, 0x8e, 0x3a, 0x2d, 0xc7, 0x39, 0xbf, 0x56, 0x28, 0x97, 0xd9, 0x20,
+ 0x4f, 0x00, 0x51, 0x3b,
+ };
+
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *a = NULL, *rrsig = NULL, *dnskey = NULL;
+ _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
+ DnssecResult result;
+
+ a = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, "nAsA.gov");
+ assert_se(a);
+
+ a->a.in_addr.s_addr = inet_addr("52.0.14.116");
+
+ log_info("A: %s", strna(dns_resource_record_to_string(a)));
+
+ rrsig = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_RRSIG, "NaSa.GOV.");
+ assert_se(rrsig);
+
+ rrsig->rrsig.type_covered = DNS_TYPE_A;
+ rrsig->rrsig.algorithm = DNSSEC_ALGORITHM_RSASHA256;
+ rrsig->rrsig.labels = 2;
+ rrsig->rrsig.original_ttl = 600;
+ rrsig->rrsig.expiration = 0x5683135c;
+ rrsig->rrsig.inception = 0x565b7da8;
+ rrsig->rrsig.key_tag = 63876;
+ rrsig->rrsig.signer = strdup("Nasa.Gov.");
+ assert_se(rrsig->rrsig.signer);
+ rrsig->rrsig.signature_size = sizeof(signature_blob);
+ rrsig->rrsig.signature = memdup(signature_blob, rrsig->rrsig.signature_size);
+ assert_se(rrsig->rrsig.signature);
+
+ log_info("RRSIG: %s", strna(dns_resource_record_to_string(rrsig)));
+
+ dnskey = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, "nASA.gOV");
+ assert_se(dnskey);
+
+ dnskey->dnskey.flags = 256;
+ dnskey->dnskey.protocol = 3;
+ dnskey->dnskey.algorithm = DNSSEC_ALGORITHM_RSASHA256;
+ dnskey->dnskey.key_size = sizeof(dnskey_blob);
+ dnskey->dnskey.key = memdup(dnskey_blob, sizeof(dnskey_blob));
+ assert_se(dnskey->dnskey.key);
+
+ log_info("DNSKEY: %s", strna(dns_resource_record_to_string(dnskey)));
+ log_info("DNSKEY keytag: %u", dnssec_keytag(dnskey, false));
+
+ assert_se(dnssec_key_match_rrsig(a->key, rrsig) > 0);
+ assert_se(dnssec_rrsig_match_dnskey(rrsig, dnskey, false) > 0);
+
+ answer = dns_answer_new(1);
+ assert_se(answer);
+ assert_se(dns_answer_add(answer, a, 0, DNS_ANSWER_AUTHENTICATED) >= 0);
+
+ /* Validate the RR as it if was 2015-12-2 today */
+ assert_se(dnssec_verify_rrset(answer, a->key, rrsig, dnskey, 1449092754*USEC_PER_SEC, &result) >= 0);
+ assert_se(result == DNSSEC_VALIDATED);
+}
+
+static void test_dnssec_verify_dns_key(void) {
+
+ static const uint8_t ds1_fprint[] = {
+ 0x46, 0x8B, 0xC8, 0xDD, 0xC7, 0xE8, 0x27, 0x03, 0x40, 0xBB, 0x8A, 0x1F, 0x3B, 0x2E, 0x45, 0x9D,
+ 0x80, 0x67, 0x14, 0x01,
+ };
+ static const uint8_t ds2_fprint[] = {
+ 0x8A, 0xEE, 0x80, 0x47, 0x05, 0x5F, 0x83, 0xD1, 0x48, 0xBA, 0x8F, 0xF6, 0xDD, 0xA7, 0x60, 0xCE,
+ 0x94, 0xF7, 0xC7, 0x5E, 0x52, 0x4C, 0xF2, 0xE9, 0x50, 0xB9, 0x2E, 0xCB, 0xEF, 0x96, 0xB9, 0x98,
+ };
+ static const uint8_t dnskey_blob[] = {
+ 0x03, 0x01, 0x00, 0x01, 0xa8, 0x12, 0xda, 0x4f, 0xd2, 0x7d, 0x54, 0x14, 0x0e, 0xcc, 0x5b, 0x5e,
+ 0x45, 0x9c, 0x96, 0x98, 0xc0, 0xc0, 0x85, 0x81, 0xb1, 0x47, 0x8c, 0x7d, 0xe8, 0x39, 0x50, 0xcc,
+ 0xc5, 0xd0, 0xf2, 0x00, 0x81, 0x67, 0x79, 0xf6, 0xcc, 0x9d, 0xad, 0x6c, 0xbb, 0x7b, 0x6f, 0x48,
+ 0x97, 0x15, 0x1c, 0xfd, 0x0b, 0xfe, 0xd3, 0xd7, 0x7d, 0x9f, 0x81, 0x26, 0xd3, 0xc5, 0x65, 0x49,
+ 0xcf, 0x46, 0x62, 0xb0, 0x55, 0x6e, 0x47, 0xc7, 0x30, 0xef, 0x51, 0xfb, 0x3e, 0xc6, 0xef, 0xde,
+ 0x27, 0x3f, 0xfa, 0x57, 0x2d, 0xa7, 0x1d, 0x80, 0x46, 0x9a, 0x5f, 0x14, 0xb3, 0xb0, 0x2c, 0xbe,
+ 0x72, 0xca, 0xdf, 0xb2, 0xff, 0x36, 0x5b, 0x4f, 0xec, 0x58, 0x8e, 0x8d, 0x01, 0xe9, 0xa9, 0xdf,
+ 0xb5, 0x60, 0xad, 0x52, 0x4d, 0xfc, 0xa9, 0x3e, 0x8d, 0x35, 0x95, 0xb3, 0x4e, 0x0f, 0xca, 0x45,
+ 0x1b, 0xf7, 0xef, 0x3a, 0x88, 0x25, 0x08, 0xc7, 0x4e, 0x06, 0xc1, 0x62, 0x1a, 0xce, 0xd8, 0x77,
+ 0xbd, 0x02, 0x65, 0xf8, 0x49, 0xfb, 0xce, 0xf6, 0xa8, 0x09, 0xfc, 0xde, 0xb2, 0x09, 0x9d, 0x39,
+ 0xf8, 0x63, 0x9c, 0x32, 0x42, 0x7c, 0xa0, 0x30, 0x86, 0x72, 0x7a, 0x4a, 0xc6, 0xd4, 0xb3, 0x2d,
+ 0x24, 0xef, 0x96, 0x3f, 0xc2, 0xda, 0xd3, 0xf2, 0x15, 0x6f, 0xda, 0x65, 0x4b, 0x81, 0x28, 0x68,
+ 0xf4, 0xfe, 0x3e, 0x71, 0x4f, 0x50, 0x96, 0x72, 0x58, 0xa1, 0x89, 0xdd, 0x01, 0x61, 0x39, 0x39,
+ 0xc6, 0x76, 0xa4, 0xda, 0x02, 0x70, 0x3d, 0xc0, 0xdc, 0x8d, 0x70, 0x72, 0x04, 0x90, 0x79, 0xd4,
+ 0xec, 0x65, 0xcf, 0x49, 0x35, 0x25, 0x3a, 0x14, 0x1a, 0x45, 0x20, 0xeb, 0x31, 0xaf, 0x92, 0xba,
+ 0x20, 0xd3, 0xcd, 0xa7, 0x13, 0x44, 0xdc, 0xcf, 0xf0, 0x27, 0x34, 0xb9, 0xe7, 0x24, 0x6f, 0x73,
+ 0xe7, 0xea, 0x77, 0x03,
+ };
+
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *dnskey = NULL, *ds1 = NULL, *ds2 = NULL;
+
+ /* The two DS RRs in effect for nasa.gov on 2015-12-01. */
+ ds1 = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, "nasa.gov");
+ assert_se(ds1);
+
+ ds1->ds.key_tag = 47857;
+ ds1->ds.algorithm = DNSSEC_ALGORITHM_RSASHA256;
+ ds1->ds.digest_type = DNSSEC_DIGEST_SHA1;
+ ds1->ds.digest_size = sizeof(ds1_fprint);
+ ds1->ds.digest = memdup(ds1_fprint, ds1->ds.digest_size);
+ assert_se(ds1->ds.digest);
+
+ log_info("DS1: %s", strna(dns_resource_record_to_string(ds1)));
+
+ ds2 = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, "NASA.GOV");
+ assert_se(ds2);
+
+ ds2->ds.key_tag = 47857;
+ ds2->ds.algorithm = DNSSEC_ALGORITHM_RSASHA256;
+ ds2->ds.digest_type = DNSSEC_DIGEST_SHA256;
+ ds2->ds.digest_size = sizeof(ds2_fprint);
+ ds2->ds.digest = memdup(ds2_fprint, ds2->ds.digest_size);
+ assert_se(ds2->ds.digest);
+
+ log_info("DS2: %s", strna(dns_resource_record_to_string(ds2)));
+
+ dnskey = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, "nasa.GOV");
+ assert_se(dnskey);
+
+ dnskey->dnskey.flags = 257;
+ dnskey->dnskey.protocol = 3;
+ dnskey->dnskey.algorithm = DNSSEC_ALGORITHM_RSASHA256;
+ dnskey->dnskey.key_size = sizeof(dnskey_blob);
+ dnskey->dnskey.key = memdup(dnskey_blob, sizeof(dnskey_blob));
+ assert_se(dnskey->dnskey.key);
+
+ log_info("DNSKEY: %s", strna(dns_resource_record_to_string(dnskey)));
+ log_info("DNSKEY keytag: %u", dnssec_keytag(dnskey, false));
+
+ assert_se(dnssec_verify_dnskey_by_ds(dnskey, ds1, false) > 0);
+ assert_se(dnssec_verify_dnskey_by_ds(dnskey, ds2, false) > 0);
+}
+
+static void test_dnssec_canonicalize_one(const char *original, const char *canonical, int r) {
+ char canonicalized[DNSSEC_CANONICAL_HOSTNAME_MAX];
+
+ assert_se(dnssec_canonicalize(original, canonicalized, sizeof(canonicalized)) == r);
+ if (r < 0)
+ return;
+
+ assert_se(streq(canonicalized, canonical));
+}
+
+static void test_dnssec_canonicalize(void) {
+ test_dnssec_canonicalize_one("", ".", 1);
+ test_dnssec_canonicalize_one(".", ".", 1);
+ test_dnssec_canonicalize_one("foo", "foo.", 4);
+ test_dnssec_canonicalize_one("foo.", "foo.", 4);
+ test_dnssec_canonicalize_one("FOO.", "foo.", 4);
+ test_dnssec_canonicalize_one("FOO.bar.", "foo.bar.", 8);
+ test_dnssec_canonicalize_one("FOO..bar.", NULL, -EINVAL);
+}
+
+static void test_dnssec_nsec3_hash(void) {
+ static const uint8_t salt[] = { 0xB0, 0x1D, 0xFA, 0xCE };
+ static const uint8_t next_hashed_name[] = { 0x84, 0x10, 0x26, 0x53, 0xc9, 0xfa, 0x4d, 0x85, 0x6c, 0x97, 0x82, 0xe2, 0x8f, 0xdf, 0x2d, 0x5e, 0x87, 0x69, 0xc4, 0x52 };
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+ uint8_t h[DNSSEC_HASH_SIZE_MAX];
+ _cleanup_free_ char *b = NULL;
+ int k;
+
+ /* The NSEC3 RR for eurid.eu on 2015-12-14. */
+ rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_NSEC3, "PJ8S08RR45VIQDAQGE7EN3VHKNROTBMM.eurid.eu.");
+ assert_se(rr);
+
+ rr->nsec3.algorithm = DNSSEC_DIGEST_SHA1;
+ rr->nsec3.flags = 1;
+ rr->nsec3.iterations = 1;
+ rr->nsec3.salt = memdup(salt, sizeof(salt));
+ assert_se(rr->nsec3.salt);
+ rr->nsec3.salt_size = sizeof(salt);
+ rr->nsec3.next_hashed_name = memdup(next_hashed_name, sizeof(next_hashed_name));
+ assert_se(rr->nsec3.next_hashed_name);
+ rr->nsec3.next_hashed_name_size = sizeof(next_hashed_name);
+
+ log_info("NSEC3: %s", strna(dns_resource_record_to_string(rr)));
+
+ k = dnssec_nsec3_hash(rr, "eurid.eu", &h);
+ assert_se(k >= 0);
+
+ b = base32hexmem(h, k, false);
+ assert_se(b);
+ assert_se(strcasecmp(b, "PJ8S08RR45VIQDAQGE7EN3VHKNROTBMM") == 0);
+}
+
+int main(int argc, char*argv[]) {
+
+ test_dnssec_canonicalize();
+ test_dnssec_verify_dns_key();
+ test_dnssec_verify_rrset();
+ test_dnssec_verify_rrset2();
+ test_dnssec_nsec3_hash();
+
+ return 0;
+}
diff --git a/src/rfkill/rfkill.c b/src/rfkill/rfkill.c
index 904dec6bfc..5c45a3ae6c 100644
--- a/src/rfkill/rfkill.c
+++ b/src/rfkill/rfkill.c
@@ -19,124 +19,410 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
-#include "mkdir.h"
-#include "fileio.h"
+#include <linux/rfkill.h>
+#include <poll.h>
+
#include "libudev.h"
+#include "sd-daemon.h"
+
+#include "alloc-util.h"
+#include "escape.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "io-util.h"
+#include "mkdir.h"
+#include "parse-util.h"
+#include "proc-cmdline.h"
+#include "string-table.h"
+#include "string-util.h"
#include "udev-util.h"
+#include "util.h"
-int main(int argc, char *argv[]) {
- _cleanup_udev_unref_ struct udev *udev = NULL;
- _cleanup_udev_device_unref_ struct udev_device *device = NULL;
- _cleanup_free_ char *saved = NULL, *escaped_type = NULL, *escaped_path_id = NULL;
- const char *name, *type, *path_id;
- int r;
+#define EXIT_USEC (5 * USEC_PER_SEC)
- if (argc != 3) {
- log_error("This program requires two arguments.");
- return EXIT_FAILURE;
- }
+static const char* const rfkill_type_table[NUM_RFKILL_TYPES] = {
+ [RFKILL_TYPE_ALL] = "all",
+ [RFKILL_TYPE_WLAN] = "wlan",
+ [RFKILL_TYPE_BLUETOOTH] = "bluetooth",
+ [RFKILL_TYPE_UWB] = "uwb",
+ [RFKILL_TYPE_WIMAX] = "wimax",
+ [RFKILL_TYPE_WWAN] = "wwan",
+ [RFKILL_TYPE_GPS] = "gps",
+ [RFKILL_TYPE_FM] = "fm",
+ [RFKILL_TYPE_NFC] = "nfc",
+};
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(rfkill_type, int);
- umask(0022);
+static int find_device(
+ struct udev *udev,
+ const struct rfkill_event *event,
+ struct udev_device **ret) {
- r = mkdir_p("/var/lib/systemd/rfkill", 0755);
- if (r < 0) {
- log_error_errno(r, "Failed to create rfkill directory: %m");
- return EXIT_FAILURE;
- }
+ _cleanup_free_ char *sysname = NULL;
+ struct udev_device *device;
+ const char *name;
- udev = udev_new();
- if (!udev) {
- log_oom();
- return EXIT_FAILURE;
- }
+ assert(udev);
+ assert(event);
+ assert(ret);
- device = udev_device_new_from_subsystem_sysname(udev, "rfkill", argv[2]);
- if (!device) {
- log_debug_errno(errno, "Failed to get rfkill device '%s', ignoring: %m", argv[2]);
- return EXIT_SUCCESS;
- }
+ if (asprintf(&sysname, "rfkill%i", event->idx) < 0)
+ return log_oom();
+
+ device = udev_device_new_from_subsystem_sysname(udev, "rfkill", sysname);
+ if (!device)
+ return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno, "Failed to open device: %m");
name = udev_device_get_sysattr_value(device, "name");
if (!name) {
- log_error("rfkill device has no name? Ignoring device.");
- return EXIT_SUCCESS;
+ log_debug("Device has no name, ignoring.");
+ udev_device_unref(device);
+ return -ENOENT;
}
log_debug("Operating on rfkill device '%s'.", name);
- type = udev_device_get_sysattr_value(device, "type");
- if (!type) {
- log_error("rfkill device has no type? Ignoring device.");
- return EXIT_SUCCESS;
+ *ret = device;
+ return 0;
+}
+
+static int wait_for_initialized(
+ struct udev *udev,
+ struct udev_device *device,
+ struct udev_device **ret) {
+
+ _cleanup_udev_monitor_unref_ struct udev_monitor *monitor = NULL;
+ struct udev_device *d;
+ const char *sysname;
+ int watch_fd, r;
+
+ assert(udev);
+ assert(device);
+ assert(ret);
+
+ if (udev_device_get_is_initialized(device) != 0) {
+ *ret = udev_device_ref(device);
+ return 0;
}
- escaped_type = cescape(type);
- if (!escaped_type) {
- log_oom();
- return EXIT_FAILURE;
+ assert_se(sysname = udev_device_get_sysname(device));
+
+ /* Wait until the device is initialized, so that we can get
+ * access to the ID_PATH property */
+
+ monitor = udev_monitor_new_from_netlink(udev, "udev");
+ if (!monitor)
+ return log_error_errno(errno, "Failed to acquire monitor: %m");
+
+ r = udev_monitor_filter_add_match_subsystem_devtype(monitor, "rfkill", NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add rfkill udev match to monitor: %m");
+
+ r = udev_monitor_enable_receiving(monitor);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enable udev receiving: %m");
+
+ watch_fd = udev_monitor_get_fd(monitor);
+ if (watch_fd < 0)
+ return log_error_errno(watch_fd, "Failed to get watch fd: %m");
+
+ /* Check again, maybe things changed */
+ d = udev_device_new_from_subsystem_sysname(udev, "rfkill", sysname);
+ if (!d)
+ return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno, "Failed to open device: %m");
+
+ if (udev_device_get_is_initialized(d) != 0) {
+ *ret = d;
+ return 0;
+ }
+
+ for (;;) {
+ _cleanup_udev_device_unref_ struct udev_device *t = NULL;
+
+ r = fd_wait_for_event(watch_fd, POLLIN, USEC_INFINITY);
+ if (r == -EINTR)
+ continue;
+ if (r < 0)
+ return log_error_errno(r, "Failed to watch udev monitor: %m");
+
+ t = udev_monitor_receive_device(monitor);
+ if (!t)
+ continue;
+
+ if (streq_ptr(udev_device_get_sysname(device), sysname)) {
+ *ret = udev_device_ref(t);
+ return 0;
+ }
}
+}
+
+static int determine_state_file(
+ struct udev *udev,
+ const struct rfkill_event *event,
+ struct udev_device *d,
+ char **ret) {
+
+ _cleanup_udev_device_unref_ struct udev_device *device = NULL;
+ const char *path_id, *type;
+ char *state_file;
+ int r;
+
+ assert(event);
+ assert(d);
+ assert(ret);
+
+ r = wait_for_initialized(udev, d, &device);
+ if (r < 0)
+ return r;
+
+ assert_se(type = rfkill_type_to_string(event->type));
path_id = udev_device_get_property_value(device, "ID_PATH");
if (path_id) {
+ _cleanup_free_ char *escaped_path_id = NULL;
+
escaped_path_id = cescape(path_id);
- if (!escaped_path_id) {
- log_oom();
- return EXIT_FAILURE;
- }
+ if (!escaped_path_id)
+ return log_oom();
- saved = strjoin("/var/lib/systemd/rfkill/", escaped_path_id, ":", escaped_type, NULL);
+ state_file = strjoin("/var/lib/systemd/rfkill/", escaped_path_id, ":", type, NULL);
} else
- saved = strjoin("/var/lib/systemd/rfkill/", escaped_type, NULL);
+ state_file = strjoin("/var/lib/systemd/rfkill/", type, NULL);
+
+ if (!state_file)
+ return log_oom();
+
+ *ret = state_file;
+ return 0;
+}
+
+static int load_state(
+ int rfkill_fd,
+ struct udev *udev,
+ const struct rfkill_event *event) {
+
+ _cleanup_udev_device_unref_ struct udev_device *device = NULL;
+ _cleanup_free_ char *state_file = NULL, *value = NULL;
+ struct rfkill_event we;
+ ssize_t l;
+ int b, r;
- if (!saved) {
- log_oom();
+ assert(rfkill_fd >= 0);
+ assert(udev);
+ assert(event);
+
+ if (shall_restore_state() == 0)
+ return 0;
+
+ r = find_device(udev, event, &device);
+ if (r < 0)
+ return r;
+
+ r = determine_state_file(udev, event, device, &state_file);
+ if (r < 0)
+ return r;
+
+ r = read_one_line_file(state_file, &value);
+ if (r == -ENOENT) {
+ /* No state file? Then save the current state */
+
+ r = write_string_file(state_file, one_zero(event->soft), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write state file %s: %m", state_file);
+
+ log_debug("Saved state '%s' to %s.", one_zero(event->soft), state_file);
+ return 0;
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to read state file %s: %m", state_file);
+
+ b = parse_boolean(value);
+ if (b < 0)
+ return log_error_errno(b, "Failed to parse state file %s: %m", state_file);
+
+ we = (struct rfkill_event) {
+ .op = RFKILL_OP_CHANGE,
+ .idx = event->idx,
+ .soft = b,
+ };
+
+ l = write(rfkill_fd, &we, sizeof(we));
+ if (l < 0)
+ return log_error_errno(errno, "Failed to restore rfkill state for %i: %m", event->idx);
+ if (l != sizeof(we)) {
+ log_error("Couldn't write rfkill event structure, too short.");
+ return -EIO;
+ }
+
+ log_debug("Loaded state '%s' from %s.", one_zero(b), state_file);
+ return 0;
+}
+
+static int save_state(
+ int rfkill_fd,
+ struct udev *udev,
+ const struct rfkill_event *event) {
+
+ _cleanup_udev_device_unref_ struct udev_device *device = NULL;
+ _cleanup_free_ char *state_file = NULL;
+ int r;
+
+ assert(rfkill_fd >= 0);
+ assert(udev);
+ assert(event);
+
+ r = find_device(udev, event, &device);
+ if (r < 0)
+ return r;
+
+ r = determine_state_file(udev, event, device, &state_file);
+ if (r < 0)
+ return r;
+
+ r = write_string_file(state_file, one_zero(event->soft), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write state file %s: %m", state_file);
+
+ log_debug("Saved state '%s' to %s.", one_zero(event->soft), state_file);
+ return 0;
+}
+
+int main(int argc, char *argv[]) {
+ _cleanup_udev_unref_ struct udev *udev = NULL;
+ _cleanup_close_ int rfkill_fd = -1;
+ bool ready = false;
+ int r, n;
+
+ if (argc > 1) {
+ log_error("This program requires no arguments.");
return EXIT_FAILURE;
}
- if (streq(argv[1], "load")) {
- _cleanup_free_ char *value = NULL;
+ log_set_target(LOG_TARGET_AUTO);
+ log_parse_environment();
+ log_open();
- if (!shall_restore_state())
- return EXIT_SUCCESS;
+ umask(0022);
- r = read_one_line_file(saved, &value);
- if (r == -ENOENT)
- return EXIT_SUCCESS;
- if (r < 0) {
- log_error_errno(r, "Failed to read %s: %m", saved);
- return EXIT_FAILURE;
+ udev = udev_new();
+ if (!udev) {
+ r = log_oom();
+ goto finish;
+ }
+
+ r = mkdir_p("/var/lib/systemd/rfkill", 0755);
+ if (r < 0) {
+ log_error_errno(r, "Failed to create rfkill directory: %m");
+ goto finish;
+ }
+
+ n = sd_listen_fds(false);
+ if (n < 0) {
+ r = log_error_errno(n, "Failed to determine whether we got any file descriptors passed: %m");
+ goto finish;
+ }
+ if (n > 1) {
+ log_error("Got too many file descriptors.");
+ r = -EINVAL;
+ goto finish;
+ }
+
+ if (n == 0) {
+ rfkill_fd = open("/dev/rfkill", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
+ if (rfkill_fd < 0) {
+ if (errno == ENOENT) {
+ log_debug_errno(errno, "Missing rfkill subsystem, or no device present, exiting.");
+ r = 0;
+ goto finish;
+ }
+
+ r = log_error_errno(errno, "Failed to open /dev/rfkill: %m");
+ goto finish;
}
+ } else {
+ rfkill_fd = SD_LISTEN_FDS_START;
- r = udev_device_set_sysattr_value(device, "soft", value);
+ r = fd_nonblock(rfkill_fd, 1);
if (r < 0) {
- log_debug_errno(r, "Failed to write 'soft' attribute on rfkill device, ignoring: %m");
- return EXIT_SUCCESS;
+ log_error_errno(r, "Failed to make /dev/rfkill socket non-blocking: %m");
+ goto finish;
}
+ }
+
+ for (;;) {
+ struct rfkill_event event;
+ const char *type;
+ ssize_t l;
- } else if (streq(argv[1], "save")) {
- const char *value;
+ l = read(rfkill_fd, &event, sizeof(event));
+ if (l < 0) {
+ if (errno == EAGAIN) {
- value = udev_device_get_sysattr_value(device, "soft");
- if (!value) {
- log_debug_errno(r, "Failed to read system attribute, ignoring device: %m");
- return EXIT_SUCCESS;
+ if (!ready) {
+ /* Notify manager that we are
+ * now finished with
+ * processing whatever was
+ * queued */
+ (void) sd_notify(false, "READY=1");
+ ready = true;
+ }
+
+ /* Hang around for a bit, maybe there's more coming */
+
+ r = fd_wait_for_event(rfkill_fd, POLLIN, EXIT_USEC);
+ if (r == -EINTR)
+ continue;
+ if (r < 0) {
+ log_error_errno(r, "Failed to poll() on device: %m");
+ goto finish;
+ }
+ if (r > 0)
+ continue;
+
+ log_debug("All events read and idle, exiting.");
+ break;
+ }
+
+ log_error_errno(errno, "Failed to read from /dev/rfkill: %m");
}
- r = write_string_file(saved, value, WRITE_STRING_FILE_CREATE);
- if (r < 0) {
- log_error_errno(r, "Failed to write %s: %m", saved);
- return EXIT_FAILURE;
+ if (l != RFKILL_EVENT_SIZE_V1) {
+ log_error("Read event structure of invalid size.");
+ r = -EIO;
+ goto finish;
}
- } else {
- log_error("Unknown verb %s.", argv[1]);
- return EXIT_FAILURE;
+ type = rfkill_type_to_string(event.type);
+ if (!type) {
+ log_debug("An rfkill device of unknown type %i discovered, ignoring.", event.type);
+ continue;
+ }
+
+ switch (event.op) {
+
+ case RFKILL_OP_ADD:
+ log_debug("A new rfkill device has been added with index %i and type %s.", event.idx, type);
+ (void) load_state(rfkill_fd, udev, &event);
+ break;
+
+ case RFKILL_OP_DEL:
+ log_debug("An rfkill device has been removed with index %i and type %s", event.idx, type);
+ break;
+
+ case RFKILL_OP_CHANGE:
+ log_debug("An rfkill device has changed state with index %i and type %s", event.idx, type);
+ (void) save_state(rfkill_fd, udev, &event);
+ break;
+
+ default:
+ log_debug("Unknown event %i from /dev/rfkill for index %i and type %s, ignoring.", event.op, event.idx, type);
+ break;
+ }
}
- return EXIT_SUCCESS;
+ r = 0;
+
+finish:
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/run/run.c b/src/run/run.c
index 657c6fcaf1..92a1d5373c 100644
--- a/src/run/run.c
+++ b/src/run/run.c
@@ -19,24 +19,28 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
#include <getopt.h>
+#include <stdio.h>
#include "sd-bus.h"
#include "sd-event.h"
+
+#include "alloc-util.h"
+#include "bus-error.h"
#include "bus-util.h"
-#include "event-util.h"
-#include "strv.h"
-#include "build.h"
-#include "unit-name.h"
+#include "calendarspec.h"
#include "env-util.h"
+#include "fd-util.h"
+#include "formats-util.h"
+#include "parse-util.h"
#include "path-util.h"
-#include "bus-error.h"
-#include "calendarspec.h"
#include "ptyfwd.h"
-#include "formats-util.h"
#include "signal-util.h"
#include "spawn-polkit-agent.h"
+#include "strv.h"
+#include "terminal-util.h"
+#include "unit-name.h"
+#include "user-util.h"
static bool arg_ask_password = true;
static bool arg_scope = false;
@@ -62,7 +66,7 @@ static usec_t arg_on_boot = 0;
static usec_t arg_on_startup = 0;
static usec_t arg_on_unit_active = 0;
static usec_t arg_on_unit_inactive = 0;
-static char *arg_on_calendar = NULL;
+static const char *arg_on_calendar = NULL;
static char **arg_timer_property = NULL;
static bool arg_quiet = false;
@@ -181,7 +185,6 @@ static int parse_argv(int argc, char *argv[]) {
};
int r, c;
- CalendarSpec *spec = NULL;
assert(argc >= 0);
assert(argv);
@@ -199,9 +202,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case ARG_USER:
arg_user = true;
@@ -337,16 +338,19 @@ static int parse_argv(int argc, char *argv[]) {
break;
- case ARG_ON_CALENDAR:
+ case ARG_ON_CALENDAR: {
+ CalendarSpec *spec = NULL;
r = calendar_spec_from_string(optarg, &spec);
if (r < 0) {
log_error("Invalid calendar spec: %s", optarg);
return r;
}
- free(spec);
+
+ calendar_spec_free(spec);
arg_on_calendar = optarg;
break;
+ }
case ARG_TIMER_PROPERTY:
@@ -391,6 +395,11 @@ static int parse_argv(int argc, char *argv[]) {
return -EINVAL;
}
+ if (arg_pty && arg_transport == BUS_TRANSPORT_REMOTE) {
+ log_error("--pty is only supported when connecting to the local system or containers.");
+ return -EINVAL;
+ }
+
if (arg_scope && with_timer()) {
log_error("Timer options are not supported in --scope mode.");
return -EINVAL;
@@ -638,6 +647,11 @@ static int transient_timer_set_properties(sd_bus_message *m) {
if (r < 0)
return r;
+ /* Automatically clean up our transient timers */
+ r = sd_bus_message_append(m, "(sv)", "RemainAfterElapse", "b", false);
+ if (r < 0)
+ return r;
+
if (arg_on_active) {
r = sd_bus_message_append(m, "(sv)", "OnActiveSec", "t", arg_on_active);
if (r < 0)
@@ -677,12 +691,57 @@ static int transient_timer_set_properties(sd_bus_message *m) {
return 0;
}
+static int make_unit_name(sd_bus *bus, UnitType t, char **ret) {
+ const char *unique, *id;
+ char *p;
+ int r;
+
+ assert(bus);
+ assert(t >= 0);
+ assert(t < _UNIT_TYPE_MAX);
+
+ r = sd_bus_get_unique_name(bus, &unique);
+ if (r < 0) {
+ sd_id128_t rnd;
+
+ /* We couldn't get the unique name, which is a pretty
+ * common case if we are connected to systemd
+ * directly. In that case, just pick a random uuid as
+ * name */
+
+ r = sd_id128_randomize(&rnd);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate random run unit name: %m");
+
+ if (asprintf(ret, "run-r" SD_ID128_FORMAT_STR ".%s", SD_ID128_FORMAT_VAL(rnd), unit_type_to_string(t)) < 0)
+ return log_oom();
+
+ return 0;
+ }
+
+ /* We managed to get the unique name, then let's use that to
+ * name our transient units. */
+
+ id = startswith(unique, ":1.");
+ if (!id) {
+ log_error("Unique name %s has unexpected format.", unique);
+ return -EINVAL;
+ }
+
+ p = strjoin("run-u", id, ".", unit_type_to_string(t), NULL);
+ if (!p)
+ return log_oom();
+
+ *ret = p;
+ return 0;
+}
+
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_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
_cleanup_free_ char *service = NULL, *pty_path = NULL;
_cleanup_close_ int master = -1;
@@ -706,12 +765,12 @@ static int start_transient_service(
return log_error_errno(errno, "Failed to unlock tty: %m");
} else if (arg_transport == BUS_TRANSPORT_MACHINE) {
- _cleanup_bus_unref_ sd_bus *system_bus = NULL;
+ _cleanup_(sd_bus_unrefp) sd_bus *system_bus = NULL;
const char *s;
- r = sd_bus_open_system(&system_bus);
+ r = sd_bus_default_system(&system_bus);
if (r < 0)
- log_error_errno(r, "Failed to connect to system bus: %m");
+ return log_error_errno(r, "Failed to connect to system bus: %m");
r = sd_bus_call_method(system_bus,
"org.freedesktop.machine1",
@@ -753,8 +812,11 @@ static int start_transient_service(
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();
+ } else {
+ r = make_unit_name(bus, UNIT_SERVICE, &service);
+ if (r < 0)
+ return r;
+ }
r = sd_bus_message_new_method_call(
bus,
@@ -796,10 +858,8 @@ static int start_transient_service(
polkit_agent_open_if_enabled();
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 (r < 0)
+ return log_error_errno(r, "Failed to start transient service unit: %s", bus_error_message(&error, r));
if (w) {
const char *object;
@@ -815,7 +875,7 @@ static int start_transient_service(
if (master >= 0) {
_cleanup_(pty_forward_freep) PTYForward *forward = NULL;
- _cleanup_event_unref_ sd_event *event = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
char last_char = 0;
r = sd_event_default(&event);
@@ -830,7 +890,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, false, &forward);
+ r = pty_forward_new(event, master, PTY_FORWARD_IGNORE_INITIAL_VHANGUP, &forward);
if (r < 0)
return log_error_errno(r, "Failed to create PTY forwarder: %m");
@@ -855,8 +915,8 @@ static int start_transient_scope(
sd_bus *bus,
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_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) 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_free_ char *scope = NULL;
@@ -874,8 +934,11 @@ static int start_transient_scope(
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();
+ } else {
+ r = make_unit_name(bus, UNIT_SCOPE, &scope);
+ if (r < 0)
+ return r;
+ }
r = sd_bus_message_new_method_call(
bus,
@@ -996,8 +1059,8 @@ static int start_transient_timer(
sd_bus *bus,
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_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) 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;
@@ -1044,9 +1107,15 @@ static int start_transient_timer(
break;
}
- } else if ((asprintf(&service, "run-"PID_FMT".service", getpid()) < 0) ||
- (asprintf(&timer, "run-"PID_FMT".timer", getpid()) < 0))
- return log_oom();
+ } else {
+ r = make_unit_name(bus, UNIT_SERVICE, &service);
+ if (r < 0)
+ return r;
+
+ r = unit_name_change_suffix(service, ".timer", &timer);
+ if (r < 0)
+ return log_error_errno(r, "Failed to change unit suffix: %m");
+ }
r = sd_bus_message_new_method_call(
bus,
@@ -1138,7 +1207,7 @@ static int start_transient_timer(
}
int main(int argc, char* argv[]) {
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_free_ char *description = NULL, *command = NULL;
int r;
@@ -1149,14 +1218,15 @@ int main(int argc, char* argv[]) {
if (r <= 0)
goto finish;
- if (argc > optind) {
- r = find_binary(argv[optind], arg_transport == BUS_TRANSPORT_LOCAL, &command);
+ if (argc > optind && arg_transport == BUS_TRANSPORT_LOCAL) {
+ /* Patch in an absolute path */
+
+ r = find_binary(argv[optind], &command);
if (r < 0) {
- log_error_errno(r, "Failed to find executable %s%s: %m",
- argv[optind],
- arg_transport == BUS_TRANSPORT_LOCAL ? "" : " on local system");
+ log_error_errno(r, "Failed to find executable %s: %m", argv[optind]);
goto finish;
}
+
argv[optind] = command;
}
@@ -1176,7 +1246,7 @@ int main(int argc, char* argv[]) {
arg_description = description;
}
- r = bus_open_transport_systemd(arg_transport, arg_host, arg_user, &bus);
+ r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus);
if (r < 0) {
log_error_errno(r, "Failed to create bus connection: %m");
goto finish;
diff --git a/src/shared/acl-util.c b/src/shared/acl-util.c
index bd8c988751..b4028564c2 100644
--- a/src/shared/acl-util.c
+++ b/src/shared/acl-util.c
@@ -23,8 +23,11 @@
#include <stdbool.h>
#include "acl-util.h"
-#include "util.h"
+#include "alloc-util.h"
+#include "string-util.h"
#include "strv.h"
+#include "user-util.h"
+#include "util.h"
int acl_find_uid(acl_t acl, uid_t uid, acl_entry_t *entry) {
acl_entry_t i;
@@ -68,6 +71,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) {
acl_entry_t i;
int r;
+ bool need = false;
assert(acl_p);
@@ -82,17 +86,16 @@ int calc_acl_mask_if_needed(acl_t *acl_p) {
if (tag == ACL_MASK)
return 0;
- if (IN_SET(tag, ACL_USER, ACL_GROUP)) {
- if (acl_calc_mask(acl_p) < 0)
- return -errno;
-
- return 1;
- }
+ if (IN_SET(tag, ACL_USER, ACL_GROUP))
+ need = true;
}
if (r < 0)
return -errno;
- return 0;
+ if (need && acl_calc_mask(acl_p) < 0)
+ return -errno;
+
+ return need;
}
int add_base_acls_if_needed(acl_t *acl_p, const char *path) {
@@ -395,3 +398,34 @@ int acls_for_file(const char *path, acl_type_t type, acl_t new, acl_t *acl) {
old = NULL;
return 0;
}
+
+int add_acls_for_user(int fd, uid_t uid) {
+ _cleanup_(acl_freep) acl_t acl = NULL;
+ acl_entry_t entry;
+ acl_permset_t permset;
+ int r;
+
+ acl = acl_get_fd(fd);
+ if (!acl)
+ return -errno;
+
+ r = acl_find_uid(acl, uid, &entry);
+ if (r <= 0) {
+ if (acl_create_entry(&acl, &entry) < 0 ||
+ acl_set_tag_type(entry, ACL_USER) < 0 ||
+ acl_set_qualifier(entry, &uid) < 0)
+ return -errno;
+ }
+
+ /* We do not recalculate the mask unconditionally here,
+ * so that the fchmod() mask above stays intact. */
+ if (acl_get_permset(entry, &permset) < 0 ||
+ acl_add_perm(permset, ACL_READ) < 0)
+ return -errno;
+
+ r = calc_acl_mask_if_needed(&acl);
+ if (r < 0)
+ return r;
+
+ return acl_set_fd(fd, acl);
+}
diff --git a/src/shared/acl-util.h b/src/shared/acl-util.h
index cf612e8722..1d7f45e2a8 100644
--- a/src/shared/acl-util.h
+++ b/src/shared/acl-util.h
@@ -23,9 +23,9 @@
#ifdef HAVE_ACL
+#include <acl/libacl.h>
#include <stdbool.h>
#include <sys/acl.h>
-#include <acl/libacl.h>
#include "macro.h"
@@ -35,6 +35,7 @@ int add_base_acls_if_needed(acl_t *acl_p, const char *path);
int acl_search_groups(const char* path, char ***ret_groups);
int parse_acl(const 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);
+int add_acls_for_user(int fd, uid_t uid);
/* acl_free takes multiple argument types.
* Multiple cleanup functions are necessary. */
diff --git a/src/shared/acpi-fpdt.c b/src/shared/acpi-fpdt.c
index 64e50401b9..dcdef50a18 100644
--- a/src/shared/acpi-fpdt.c
+++ b/src/shared/acpi-fpdt.c
@@ -19,16 +19,18 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
-#include <fcntl.h>
-#include <util.h>
-#include <fileio.h>
-#include <time-util.h>
-#include <acpi-fpdt.h>
+#include "acpi-fpdt.h"
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "time-util.h"
struct acpi_table_header {
char signature[4];
diff --git a/src/shared/apparmor-util.c b/src/shared/apparmor-util.c
index c2bbd330bd..f8cbb333d5 100644
--- a/src/shared/apparmor-util.c
+++ b/src/shared/apparmor-util.c
@@ -19,10 +19,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <stddef.h>
-#include "util.h"
-#include "fileio.h"
+#include "alloc-util.h"
#include "apparmor-util.h"
+#include "fileio.h"
+#include "parse-util.h"
bool mac_apparmor_use(void) {
static int cached_use = -1;
diff --git a/src/shared/architecture.c b/src/shared/architecture.c
index 8e72e7a36a..ca6821b4d8 100644
--- a/src/shared/architecture.c
+++ b/src/shared/architecture.c
@@ -22,6 +22,9 @@
#include <sys/utsname.h>
#include "architecture.h"
+#include "macro.h"
+#include "string-table.h"
+#include "string-util.h"
int uname_architecture(void) {
diff --git a/src/shared/architecture.h b/src/shared/architecture.h
index f5bbf65a90..c6af4a5b33 100644
--- a/src/shared/architecture.h
+++ b/src/shared/architecture.h
@@ -23,6 +23,7 @@
#include <endian.h>
+#include "macro.h"
#include "util.h"
/* A cleaned up architecture definition. We don't want to get lost in
@@ -78,9 +79,11 @@ int uname_architecture(void);
#if defined(__x86_64__)
# define native_architecture() ARCHITECTURE_X86_64
# define LIB_ARCH_TUPLE "x86_64-linux-gnu"
+# define PROC_CPUINFO_MODEL "model name"
#elif defined(__i386__)
# define native_architecture() ARCHITECTURE_X86
# define LIB_ARCH_TUPLE "i386-linux-gnu"
+# define PROC_CPUINFO_MODEL "model name"
#elif defined(__powerpc64__)
# if __BYTE_ORDER == __BIG_ENDIAN
# define native_architecture() ARCHITECTURE_PPC64
@@ -89,6 +92,7 @@ int uname_architecture(void);
# define native_architecture() ARCHITECTURE_PPC64_LE
# define LIB_ARCH_TUPLE "powerpc64le-linux-gnu"
# endif
+# define PROC_CPUINFO_MODEL "cpu"
#elif defined(__powerpc__)
# if __BYTE_ORDER == __BIG_ENDIAN
# define native_architecture() ARCHITECTURE_PPC
@@ -97,15 +101,18 @@ int uname_architecture(void);
# define native_architecture() ARCHITECTURE_PPC_LE
# error "Missing LIB_ARCH_TUPLE for PPCLE"
# endif
+# define PROC_CPUINFO_MODEL "cpu"
#elif defined(__ia64__)
# define native_architecture() ARCHITECTURE_IA64
# define LIB_ARCH_TUPLE "ia64-linux-gnu"
#elif defined(__hppa64__)
# define native_architecture() ARCHITECTURE_PARISC64
# error "Missing LIB_ARCH_TUPLE for HPPA64"
+# define PROC_CPUINFO_MODEL "cpu"
#elif defined(__hppa__)
# define native_architecture() ARCHITECTURE_PARISC
# define LIB_ARCH_TUPLE "hppa‑linux‑gnu"
+# define PROC_CPUINFO_MODEL "cpu"
#elif defined(__s390x__)
# define native_architecture() ARCHITECTURE_S390X
# define LIB_ARCH_TUPLE "s390x-linux-gnu"
@@ -115,9 +122,11 @@ int uname_architecture(void);
#elif defined(__sparc64__)
# define native_architecture() ARCHITECTURE_SPARC64
# define LIB_ARCH_TUPLE "sparc64-linux-gnu"
+# define PROC_CPUINFO_MODEL "cpu"
#elif defined(__sparc__)
# define native_architecture() ARCHITECTURE_SPARC
# define LIB_ARCH_TUPLE "sparc-linux-gnu"
+# define PROC_CPUINFO_MODEL "cpu"
#elif defined(__mips64__)
# if __BYTE_ORDER == __BIG_ENDIAN
# define native_architecture() ARCHITECTURE_MIPS64
@@ -126,6 +135,7 @@ int uname_architecture(void);
# define native_architecture() ARCHITECTURE_MIPS64_LE
# error "Missing LIB_ARCH_TUPLE for MIPS64_LE"
# endif
+# define PROC_CPUINFO_MODEL "cpu model"
#elif defined(__mips__)
# if __BYTE_ORDER == __BIG_ENDIAN
# define native_architecture() ARCHITECTURE_MIPS
@@ -134,6 +144,7 @@ int uname_architecture(void);
# define native_architecture() ARCHITECTURE_MIPS_LE
# define LIB_ARCH_TUPLE "mipsel-linux-gnu"
# endif
+# define PROC_CPUINFO_MODEL "cpu model"
#elif defined(__alpha__)
# define native_architecture() ARCHITECTURE_ALPHA
# define LIB_ARCH_TUPLE "alpha-linux-gnu"
@@ -169,6 +180,7 @@ int uname_architecture(void);
# define LIB_ARCH_TUPLE "arm-linux-gnu"
# endif
# endif
+# define PROC_CPUINFO_MODEL "model name"
#elif defined(__sh64__)
# define native_architecture() ARCHITECTURE_SH64
# error "Missing LIB_ARCH_TUPLE for SH64"
@@ -188,5 +200,10 @@ int uname_architecture(void);
# error "Please register your architecture here!"
#endif
+#ifndef PROC_CPUINFO_MODEL
+#warning "PROC_CPUINFO_MODEL not defined for your architecture"
+#define PROC_CPUINFO_MODEL "model name"
+#endif
+
const char *architecture_to_string(int a) _const_;
int architecture_from_string(const char *s) _pure_;
diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c
index b02cdf9a17..8de1445a96 100644
--- a/src/shared/ask-password-api.c
+++ b/src/shared/ask-password-api.c
@@ -18,27 +18,179 @@
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 <termios.h>
-#include <unistd.h>
-#include <poll.h>
-#include <sys/inotify.h>
+
#include <errno.h>
#include <fcntl.h>
-#include <sys/socket.h>
-#include <string.h>
-#include <sys/un.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdbool.h>
#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/inotify.h>
#include <sys/signalfd.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <termios.h>
+#include <unistd.h>
-#include "util.h"
+#include "alloc-util.h"
+#include "ask-password-api.h"
+#include "fd-util.h"
+#include "fileio.h"
#include "formats-util.h"
+#include "io-util.h"
+#include "log.h"
+#include "macro.h"
+#include "missing.h"
#include "mkdir.h"
-#include "strv.h"
#include "random-util.h"
-#include "terminal-util.h"
#include "signal-util.h"
-#include "ask-password-api.h"
+#include "socket-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "terminal-util.h"
+#include "time-util.h"
+#include "umask-util.h"
+#include "util.h"
+
+#define KEYRING_TIMEOUT_USEC ((5 * USEC_PER_MINUTE) / 2)
+
+static int lookup_key(const char *keyname, key_serial_t *ret) {
+ key_serial_t serial;
+
+ assert(keyname);
+ assert(ret);
+
+ serial = request_key("user", keyname, NULL, 0);
+ if (serial == -1)
+ return negative_errno();
+
+ *ret = serial;
+ return 0;
+}
+
+static int retrieve_key(key_serial_t serial, char ***ret) {
+ _cleanup_free_ char *p = NULL;
+ long m = 100, n;
+ char **l;
+
+ assert(ret);
+
+ for (;;) {
+ p = new(char, m);
+ if (!p)
+ return -ENOMEM;
+
+ n = keyctl(KEYCTL_READ, (unsigned long) serial, (unsigned long) p, (unsigned long) m, 0);
+ if (n < 0)
+ return -errno;
+
+ if (n < m)
+ break;
+
+ memory_erase(p, n);
+ free(p);
+ m *= 2;
+ }
+
+ l = strv_parse_nulstr(p, n);
+ if (!l)
+ return -ENOMEM;
+
+ memory_erase(p, n);
+
+ *ret = l;
+ return 0;
+}
+
+static int add_to_keyring(const char *keyname, AskPasswordFlags flags, char **passwords) {
+ _cleanup_strv_free_erase_ char **l = NULL;
+ _cleanup_free_ char *p = NULL;
+ key_serial_t serial;
+ size_t n;
+ int r;
+
+ assert(keyname);
+ assert(passwords);
+
+ if (!(flags & ASK_PASSWORD_PUSH_CACHE))
+ return 0;
+
+ r = lookup_key(keyname, &serial);
+ if (r >= 0) {
+ r = retrieve_key(serial, &l);
+ if (r < 0)
+ return r;
+ } else if (r != -ENOKEY)
+ return r;
+
+ r = strv_extend_strv(&l, passwords, true);
+ if (r <= 0)
+ return r;
+
+ r = strv_make_nulstr(l, &p, &n);
+ if (r < 0)
+ return r;
+
+ /* Truncate trailing NUL */
+ assert(n > 0);
+ assert(p[n-1] == 0);
+
+ serial = add_key("user", keyname, p, n-1, KEY_SPEC_USER_KEYRING);
+ memory_erase(p, n);
+ if (serial == -1)
+ return -errno;
+
+ if (keyctl(KEYCTL_SET_TIMEOUT,
+ (unsigned long) serial,
+ (unsigned long) DIV_ROUND_UP(KEYRING_TIMEOUT_USEC, USEC_PER_SEC), 0, 0) < 0)
+ log_debug_errno(errno, "Failed to adjust timeout: %m");
+
+ log_debug("Added key to keyring as %" PRIi32 ".", serial);
+
+ return 1;
+}
+
+static int add_to_keyring_and_log(const char *keyname, AskPasswordFlags flags, char **passwords) {
+ int r;
+
+ assert(keyname);
+ assert(passwords);
+
+ r = add_to_keyring(keyname, flags, passwords);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to add password to keyring: %m");
+
+ return 0;
+}
+
+int ask_password_keyring(const char *keyname, AskPasswordFlags flags, char ***ret) {
+
+ key_serial_t serial;
+ int r;
+
+ assert(keyname);
+ assert(ret);
+
+ if (!(flags & ASK_PASSWORD_ACCEPT_CACHED))
+ return -EUNATCH;
+
+ r = lookup_key(keyname, &serial);
+ if (r == -ENOSYS) /* when retrieving the distinction doesn't matter */
+ return -ENOKEY;
+ if (r < 0)
+ return r;
+
+ return retrieve_key(serial, ret);
+}
static void backspace_chars(int ttyfd, size_t p) {
@@ -54,10 +206,11 @@ static void backspace_chars(int ttyfd, size_t p) {
int ask_password_tty(
const char *message,
+ const char *keyname,
usec_t until,
- bool echo,
+ AskPasswordFlags flags,
const char *flag_file,
- char **_passphrase) {
+ char **ret) {
struct termios old_termios, new_termios;
char passphrase[LINE_MAX], *x;
@@ -66,15 +219,19 @@ int ask_password_tty(
_cleanup_close_ int ttyfd = -1, notify = -1;
struct pollfd pollfd[2];
bool reset_tty = false;
- bool silent_mode = false;
bool dirty = false;
enum {
POLL_TTY,
POLL_INOTIFY
};
- assert(message);
- assert(_passphrase);
+ assert(ret);
+
+ if (flags & ASK_PASSWORD_NO_TTY)
+ return -EUNATCH;
+
+ if (!message)
+ message = "Password:";
if (flag_file) {
notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
@@ -97,10 +254,10 @@ int ask_password_tty(
goto finish;
}
- loop_write(ttyfd, ANSI_HIGHLIGHT, sizeof(ANSI_HIGHLIGHT)-1, false);
+ loop_write(ttyfd, ANSI_HIGHLIGHT, strlen(ANSI_HIGHLIGHT), false);
loop_write(ttyfd, message, strlen(message), false);
loop_write(ttyfd, " ", 1, false);
- loop_write(ttyfd, ANSI_NORMAL, sizeof(ANSI_NORMAL)-1, false);
+ loop_write(ttyfd, ANSI_NORMAL, strlen(ANSI_NORMAL), false);
new_termios = old_termios;
new_termios.c_lflag &= ~(ICANON|ECHO);
@@ -145,7 +302,7 @@ int ask_password_tty(
goto finish;
}
- k = poll(pollfd, notify > 0 ? 2 : 1, sleep_for);
+ k = poll(pollfd, notify >= 0 ? 2 : 1, sleep_for);
if (k < 0) {
if (errno == EINTR)
continue;
@@ -157,7 +314,7 @@ int ask_password_tty(
goto finish;
}
- if (notify > 0 && pollfd[POLL_INOTIFY].revents != 0)
+ if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0)
flush_fd(notify);
if (pollfd[POLL_TTY].revents == 0)
@@ -178,7 +335,7 @@ int ask_password_tty(
break;
else if (c == 21) { /* C-u */
- if (!silent_mode)
+ if (!(flags & ASK_PASSWORD_SILENT))
backspace_chars(ttyfd, p);
p = 0;
@@ -186,28 +343,28 @@ int ask_password_tty(
if (p > 0) {
- if (!silent_mode)
+ if (!(flags & ASK_PASSWORD_SILENT))
backspace_chars(ttyfd, 1);
p--;
- } else if (!dirty && !silent_mode) {
+ } else if (!dirty && !(flags & ASK_PASSWORD_SILENT)) {
- silent_mode = true;
+ flags |= ASK_PASSWORD_SILENT;
/* There are two ways to enter silent
* mode. Either by pressing backspace
- * as first key (and only as first key),
- * or ... */
+ * as first key (and only as first
+ * key), or ... */
if (ttyfd >= 0)
loop_write(ttyfd, "(no echo) ", 10, false);
} else if (ttyfd >= 0)
loop_write(ttyfd, "\a", 1, false);
- } else if (c == '\t' && !silent_mode) {
+ } else if (c == '\t' && !(flags & ASK_PASSWORD_SILENT)) {
backspace_chars(ttyfd, p);
- silent_mode = true;
+ flags |= ASK_PASSWORD_SILENT;
/* ... or by pressing TAB at any time. */
@@ -221,20 +378,26 @@ int ask_password_tty(
passphrase[p++] = c;
- if (!silent_mode && ttyfd >= 0)
- loop_write(ttyfd, echo ? &c : "*", 1, false);
+ if (!(flags & ASK_PASSWORD_SILENT) && ttyfd >= 0)
+ loop_write(ttyfd, (flags & ASK_PASSWORD_ECHO) ? &c : "*", 1, false);
dirty = true;
}
+
+ c = 'x';
}
x = strndup(passphrase, p);
+ memory_erase(passphrase, p);
if (!x) {
r = -ENOMEM;
goto finish;
}
- *_passphrase = x;
+ if (keyname)
+ (void) add_to_keyring_and_log(keyname, flags, STRV_MAKE(x));
+
+ *ret = x;
r = 0;
finish:
@@ -247,52 +410,38 @@ finish:
}
static int create_socket(char **name) {
- int fd;
- union {
- struct sockaddr sa;
- struct sockaddr_un un;
- } sa = {
+ union sockaddr_union sa = {
.un.sun_family = AF_UNIX,
};
- int one = 1;
- int r = 0;
+ _cleanup_close_ int fd = -1;
+ static const int one = 1;
char *c;
+ int r;
assert(name);
fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (fd < 0)
- return log_error_errno(errno, "socket() failed: %m");
+ return -errno;
snprintf(sa.un.sun_path, sizeof(sa.un.sun_path)-1, "/run/systemd/ask-password/sck.%" PRIx64, random_u64());
RUN_WITH_UMASK(0177) {
- r = bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path));
- }
-
- if (r < 0) {
- r = -errno;
- log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
- goto fail;
+ if (bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0)
+ return -errno;
}
- if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) {
- r = -errno;
- log_error_errno(errno, "SO_PASSCRED failed: %m");
- goto fail;
- }
+ if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0)
+ return -errno;
c = strdup(sa.un.sun_path);
- if (!c) {
- r = log_oom();
- goto fail;
- }
+ if (!c)
+ return -ENOMEM;
*name = c;
- return fd;
-fail:
- safe_close(fd);
+ r = fd;
+ fd = -1;
return r;
}
@@ -301,10 +450,10 @@ int ask_password_agent(
const char *message,
const char *icon,
const char *id,
+ const char *keyname,
usec_t until,
- bool echo,
- bool accept_cached,
- char ***_passphrases) {
+ AskPasswordFlags flags,
+ char ***ret) {
enum {
FD_SOCKET,
@@ -312,35 +461,38 @@ int ask_password_agent(
_FD_MAX
};
+ _cleanup_close_ int socket_fd = -1, signal_fd = -1, fd = -1;
char temp[] = "/run/systemd/ask-password/tmp.XXXXXX";
char final[sizeof(temp)] = "";
- _cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *socket_name = NULL;
- _cleanup_close_ int socket_fd = -1, signal_fd = -1, fd = -1;
- sigset_t mask, oldmask;
+ _cleanup_strv_free_ char **l = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
struct pollfd pollfd[_FD_MAX];
+ sigset_t mask, oldmask;
int r;
- assert(_passphrases);
+ assert(ret);
+
+ if (flags & ASK_PASSWORD_NO_AGENT)
+ return -EUNATCH;
assert_se(sigemptyset(&mask) >= 0);
assert_se(sigset_add_many(&mask, SIGINT, SIGTERM, -1) >= 0);
assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) >= 0);
- mkdir_p_label("/run/systemd/ask-password", 0755);
+ (void) mkdir_p_label("/run/systemd/ask-password", 0755);
fd = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC);
if (fd < 0) {
- r = log_error_errno(errno,
- "Failed to create password file: %m");
+ r = fd;
goto finish;
}
- fchmod(fd, 0644);
+ (void) fchmod(fd, 0644);
f = fdopen(fd, "w");
if (!f) {
- r = log_error_errno(errno, "Failed to allocate FILE: %m");
+ r = -errno;
goto finish;
}
@@ -348,7 +500,7 @@ int ask_password_agent(
signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
if (signal_fd < 0) {
- r = log_error_errno(errno, "signalfd(): %m");
+ r = -errno;
goto finish;
}
@@ -367,8 +519,8 @@ int ask_password_agent(
"NotAfter="USEC_FMT"\n",
getpid(),
socket_name,
- accept_cached ? 1 : 0,
- echo ? 1 : 0,
+ (flags & ASK_PASSWORD_ACCEPT_CACHED) ? 1 : 0,
+ (flags & ASK_PASSWORD_ECHO) ? 1 : 0,
until);
if (message)
@@ -381,10 +533,8 @@ int ask_password_agent(
fprintf(f, "Id=%s\n", id);
r = fflush_and_check(f);
- if (r < 0) {
- log_error_errno(r, "Failed to write query file: %m");
+ if (r < 0)
goto finish;
- }
memcpy(final, temp, sizeof(temp));
@@ -393,7 +543,7 @@ int ask_password_agent(
final[sizeof(final)-9] = 'k';
if (rename(temp, final) < 0) {
- r = log_error_errno(errno, "Failed to rename query file: %m");
+ r = -errno;
goto finish;
}
@@ -419,7 +569,6 @@ int ask_password_agent(
t = now(CLOCK_MONOTONIC);
if (until > 0 && until <= t) {
- log_notice("Timed out");
r = -ETIME;
goto finish;
}
@@ -429,12 +578,11 @@ int ask_password_agent(
if (errno == EINTR)
continue;
- r = log_error_errno(errno, "poll() failed: %m");
+ r = -errno;
goto finish;
}
if (k <= 0) {
- log_notice("Timed out");
r = -ETIME;
goto finish;
}
@@ -445,7 +593,6 @@ int ask_password_agent(
}
if (pollfd[FD_SOCKET].revents != POLLIN) {
- log_error("Unexpected poll() event.");
r = -EIO;
goto finish;
}
@@ -467,14 +614,14 @@ int ask_password_agent(
errno == EINTR)
continue;
- r = log_error_errno(errno, "recvmsg() failed: %m");
+ r = -errno;
goto finish;
}
cmsg_close_all(&msghdr);
if (n <= 0) {
- log_error("Message too short");
+ log_debug("Message too short");
continue;
}
@@ -482,84 +629,104 @@ int ask_password_agent(
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.");
+ log_debug("Received message without credentials. Ignoring.");
continue;
}
ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
if (ucred->uid != 0) {
- log_warning("Got request from unprivileged user. Ignoring.");
+ log_debug("Got request from unprivileged user. Ignoring.");
continue;
}
if (passphrase[0] == '+') {
- char **l;
-
+ /* An empty message refers to the empty password */
if (n == 1)
l = strv_new("", NULL);
else
l = strv_parse_nulstr(passphrase+1, n-1);
- /* An empty message refers to the empty password */
-
+ memory_erase(passphrase, n);
if (!l) {
r = -ENOMEM;
goto finish;
}
if (strv_length(l) <= 0) {
- strv_free(l);
- log_error("Invalid packet");
+ l = strv_free(l);
+ log_debug("Invalid packet");
continue;
}
- *_passphrases = l;
+ break;
+ }
- } else if (passphrase[0] == '-') {
+ if (passphrase[0] == '-') {
r = -ECANCELED;
goto finish;
- } else {
- log_error("Invalid packet");
- continue;
}
- break;
+ log_debug("Invalid packet");
}
+ if (keyname)
+ (void) add_to_keyring_and_log(keyname, flags, l);
+
+ *ret = l;
+ l = NULL;
r = 0;
finish:
if (socket_name)
- unlink(socket_name);
+ (void) unlink(socket_name);
- unlink(temp);
+ (void) unlink(temp);
if (final[0])
- unlink(final);
+ (void) unlink(final);
assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0);
-
return r;
}
-int ask_password_auto(const char *message, const char *icon, const char *id,
- usec_t until, bool accept_cached, char ***_passphrases) {
- assert(message);
- assert(_passphrases);
+int ask_password_auto(
+ const char *message,
+ const char *icon,
+ const char *id,
+ const char *keyname,
+ usec_t until,
+ AskPasswordFlags flags,
+ char ***ret) {
- if (isatty(STDIN_FILENO)) {
- int r;
- char *s = NULL, **l = NULL;
+ int r;
- r = ask_password_tty(message, until, false, NULL, &s);
- if (r < 0)
+ assert(ret);
+
+ if ((flags & ASK_PASSWORD_ACCEPT_CACHED) && keyname) {
+ r = ask_password_keyring(keyname, flags, ret);
+ if (r != -ENOKEY)
return r;
+ }
- r = strv_consume(&l, s);
+ if (!(flags & ASK_PASSWORD_NO_TTY) && isatty(STDIN_FILENO)) {
+ char *s = NULL, **l = NULL;
+
+ r = ask_password_tty(message, keyname, until, flags, NULL, &s);
if (r < 0)
return r;
- *_passphrases = l;
- return r;
- } else
- return ask_password_agent(message, icon, id, until, false, accept_cached, _passphrases);
+ r = strv_push(&l, s);
+ if (r < 0) {
+ string_erase(s);
+ free(s);
+ return -ENOMEM;
+ }
+
+ *ret = l;
+ return 0;
+ }
+
+ if (!(flags & ASK_PASSWORD_NO_AGENT))
+ return ask_password_agent(message, icon, id, keyname, until, flags, ret);
+
+ return -EUNATCH;
}
diff --git a/src/shared/ask-password-api.h b/src/shared/ask-password-api.h
index 0954e072be..913cad9f8a 100644
--- a/src/shared/ask-password-api.h
+++ b/src/shared/ask-password-api.h
@@ -21,11 +21,20 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-
-int ask_password_tty(const char *message, usec_t until, bool echo, const char *flag_file, char **_passphrase);
-
-int ask_password_agent(const char *message, const char *icon, const char *id,
- usec_t until, bool echo, bool accept_cached, char ***_passphrases);
-
-int ask_password_auto(const char *message, const char *icon, const char *id,
- usec_t until, bool accept_cached, char ***_passphrases);
+#include <stdbool.h>
+
+#include "time-util.h"
+
+typedef enum AskPasswordFlags {
+ ASK_PASSWORD_ACCEPT_CACHED = 1,
+ ASK_PASSWORD_PUSH_CACHE = 2,
+ ASK_PASSWORD_ECHO = 4, /* show the password literally while reading, instead of "*" */
+ ASK_PASSWORD_SILENT = 8, /* do no show any password at all while reading */
+ ASK_PASSWORD_NO_TTY = 16,
+ ASK_PASSWORD_NO_AGENT = 32,
+} AskPasswordFlags;
+
+int ask_password_tty(const char *message, const char *keyname, usec_t until, AskPasswordFlags flags, const char *flag_file, char **ret);
+int ask_password_agent(const char *message, const char *icon, const char *id, const char *keyname, usec_t until, AskPasswordFlags flag, char ***ret);
+int ask_password_keyring(const char *keyname, AskPasswordFlags flags, char ***ret);
+int ask_password_auto(const char *message, const char *icon, const char *id, const char *keyname, usec_t until, AskPasswordFlags flag, char ***ret);
diff --git a/src/shared/base-filesystem.c b/src/shared/base-filesystem.c
index ab6fc171b0..2a7a38dd14 100644
--- a/src/shared/base-filesystem.c
+++ b/src/shared/base-filesystem.c
@@ -20,13 +20,21 @@
***/
#include <errno.h>
-#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdbool.h>
#include <stdlib.h>
+#include <sys/stat.h>
+#include <syslog.h>
#include <unistd.h>
+#include "alloc-util.h"
#include "base-filesystem.h"
+#include "fd-util.h"
#include "log.h"
#include "macro.h"
+#include "string-util.h"
+#include "umask-util.h"
+#include "user-util.h"
#include "util.h"
typedef struct BaseFilesystem {
@@ -34,12 +42,13 @@ typedef struct BaseFilesystem {
mode_t mode;
const char *target;
const char *exists;
+ bool ignore_failure;
} BaseFilesystem;
static const BaseFilesystem table[] = {
{ "bin", 0, "usr/bin\0", NULL },
{ "lib", 0, "usr/lib\0", NULL },
- { "root", 0755, NULL, NULL },
+ { "root", 0755, NULL, NULL, true },
{ "sbin", 0, "usr/sbin\0", NULL },
{ "usr", 0755, NULL, NULL },
{ "var", 0755, NULL, NULL },
@@ -104,8 +113,13 @@ int base_filesystem_create(const char *root, uid_t uid, gid_t gid) {
RUN_WITH_UMASK(0000)
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 (r < 0 && errno != EEXIST) {
+ log_full_errno(table[i].ignore_failure ? LOG_DEBUG : LOG_ERR, errno,
+ "Failed to create directory at %s/%s: %m", root, table[i].dir);
+
+ if (!table[i].ignore_failure)
+ return -errno;
+ }
if (uid != UID_INVALID || gid != UID_INVALID) {
if (fchownat(fd, table[i].dir, uid, gid, AT_SYMLINK_NOFOLLOW) < 0)
diff --git a/src/shared/boot-timestamps.c b/src/shared/boot-timestamps.c
index ecbe1aaa0f..63daf932f0 100644
--- a/src/shared/boot-timestamps.c
+++ b/src/shared/boot-timestamps.c
@@ -20,9 +20,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "boot-timestamps.h"
#include "acpi-fpdt.h"
+#include "boot-timestamps.h"
#include "efivars.h"
+#include "macro.h"
+#include "time-util.h"
int boot_timestamps(const dual_timestamp *n, dual_timestamp *firmware, dual_timestamp *loader) {
usec_t x = 0, y = 0, a;
diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c
index 16b17c2c82..b9a8ee4074 100644
--- a/src/shared/bus-util.c
+++ b/src/shared/bus-util.c
@@ -19,29 +19,57 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/resource.h>
#include <sys/socket.h>
+#include <unistd.h>
+#include "sd-bus-protocol.h"
+#include "sd-bus.h"
#include "sd-daemon.h"
#include "sd-event.h"
-#include "sd-bus.h"
+#include "sd-id128.h"
-#include "bus-error.h"
+#include "alloc-util.h"
#include "bus-internal.h"
#include "bus-label.h"
#include "bus-message.h"
+#include "bus-util.h"
#include "cgroup-util.h"
#include "def.h"
+#include "env-util.h"
+#include "escape.h"
+#include "extract-word.h"
+#include "fd-util.h"
+#include "hashmap.h"
+#include "install.h"
+#include "kdbus.h"
+#include "log.h"
#include "macro.h"
#include "missing.h"
+#include "parse-util.h"
#include "path-util.h"
+#include "proc-cmdline.h"
+#include "process-util.h"
+#include "rlimit-util.h"
#include "set.h"
#include "signal-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
#include "strv.h"
+#include "syslog-util.h"
+#include "time-util.h"
#include "unit-name.h"
+#include "user-util.h"
+#include "utf8.h"
#include "util.h"
-#include "bus-util.h"
-
static int name_owner_change_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
sd_event *e = userdata;
@@ -169,7 +197,7 @@ int bus_event_loop_with_idle(
}
int bus_name_has_owner(sd_bus *c, const char *name, sd_bus_error *error) {
- _cleanup_bus_message_unref_ sd_bus_message *rep = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *rep = NULL;
int r, has_owner = 0;
assert(c);
@@ -195,7 +223,7 @@ int bus_name_has_owner(sd_bus *c, const char *name, sd_bus_error *error) {
}
static int check_good_user(sd_bus_message *m, uid_t good_user) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
uid_t sender_uid;
int r;
@@ -245,8 +273,8 @@ int bus_test_polkit(
return 1;
#ifdef ENABLE_POLKIT
else {
- _cleanup_bus_message_unref_ sd_bus_message *request = NULL;
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *request = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
int authorized = false, challenge = false;
const char *sender, **k, **v;
@@ -349,7 +377,7 @@ static void async_polkit_query_free(AsyncPolkitQuery *q) {
}
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;
+ _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
AsyncPolkitQuery *q = userdata;
int r;
@@ -387,7 +415,7 @@ int bus_verify_polkit_async(
sd_bus_error *error) {
#ifdef ENABLE_POLKIT
- _cleanup_bus_message_unref_ sd_bus_message *pk = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *pk = NULL;
AsyncPolkitQuery *q;
const char *sender, **k, **v;
sd_bus_message_handler_t callback;
@@ -574,14 +602,14 @@ int bus_check_peercred(sd_bus *c) {
return 1;
}
-int bus_open_system_systemd(sd_bus **_bus) {
- _cleanup_bus_unref_ sd_bus *bus = NULL;
+int bus_connect_system_systemd(sd_bus **_bus) {
+ _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
int r;
assert(_bus);
if (geteuid() != 0)
- return sd_bus_open_system(_bus);
+ return sd_bus_default_system(_bus);
/* If we are root and kdbus is not available, then let's talk
* directly to the system instance, instead of going via the
@@ -616,7 +644,7 @@ int bus_open_system_systemd(sd_bus **_bus) {
r = sd_bus_start(bus);
if (r < 0)
- return sd_bus_open_system(_bus);
+ return sd_bus_default_system(_bus);
r = bus_check_peercred(bus);
if (r < 0)
@@ -628,8 +656,8 @@ int bus_open_system_systemd(sd_bus **_bus) {
return 0;
}
-int bus_open_user_systemd(sd_bus **_bus) {
- _cleanup_bus_unref_ sd_bus *bus = NULL;
+int bus_connect_user_systemd(sd_bus **_bus) {
+ _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
_cleanup_free_ char *ee = NULL;
const char *e;
int r;
@@ -658,7 +686,7 @@ int bus_open_user_systemd(sd_bus **_bus) {
e = secure_getenv("XDG_RUNTIME_DIR");
if (!e)
- return sd_bus_open_user(_bus);
+ return sd_bus_default_user(_bus);
ee = bus_address_escape(e);
if (!ee)
@@ -674,7 +702,7 @@ int bus_open_user_systemd(sd_bus **_bus) {
r = sd_bus_start(bus);
if (r < 0)
- return sd_bus_open_user(_bus);
+ return sd_bus_default_user(_bus);
r = bus_check_peercred(bus);
if (r < 0)
@@ -895,8 +923,8 @@ int bus_print_property(const char *name, sd_bus_message *property, bool all) {
}
int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, char **filter, bool all) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert(bus);
@@ -1079,7 +1107,7 @@ int bus_message_map_all_properties(
const struct bus_properties_map *map,
void *userdata) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert(m);
@@ -1185,8 +1213,8 @@ int bus_map_all_properties(
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;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert(bus);
@@ -1209,7 +1237,7 @@ int bus_map_all_properties(
return bus_message_map_all_properties(m, map, userdata);
}
-int bus_open_transport(BusTransport transport, const char *host, bool user, sd_bus **bus) {
+int bus_connect_transport(BusTransport transport, const char *host, bool user, sd_bus **bus) {
int r;
assert(transport >= 0);
@@ -1244,7 +1272,7 @@ int bus_open_transport(BusTransport transport, const char *host, bool user, sd_b
return r;
}
-int bus_open_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus) {
+int bus_connect_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus) {
int r;
assert(transport >= 0);
@@ -1258,9 +1286,9 @@ int bus_open_transport_systemd(BusTransport transport, const char *host, bool us
case BUS_TRANSPORT_LOCAL:
if (user)
- r = bus_open_user_systemd(bus);
+ r = bus_connect_user_systemd(bus);
else
- r = bus_open_system_systemd(bus);
+ r = bus_connect_system_systemd(bus);
break;
@@ -1416,6 +1444,37 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
return bus_log_create_error(r);
return 0;
+
+ } else if (streq(field, "EnvironmentFile")) {
+
+ r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "EnvironmentFiles");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(m, "v", "a(sb)", 1,
+ eq[0] == '-' ? eq + 1 : eq,
+ eq[0] == '-');
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ return 0;
+
+ } else if (streq(field, "RandomizedDelaySec")) {
+ usec_t t;
+
+ r = parse_sec(eq, &t);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse RandomizedDelaySec= parameter: %s", eq);
+
+ r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "RandomizedDelayUSec");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(m, "v", "t", t);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ return 0;
}
r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
@@ -1426,13 +1485,12 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
"CPUAccounting", "MemoryAccounting", "BlockIOAccounting", "TasksAccounting",
"SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies",
"IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit",
- "PrivateTmp", "PrivateDevices", "PrivateNetwork", "NoNewPrivileges")) {
+ "PrivateTmp", "PrivateDevices", "PrivateNetwork", "NoNewPrivileges",
+ "SyslogLevelPrefix", "Delegate", "RemainAfterElapse")) {
r = parse_boolean(eq);
- if (r < 0) {
- log_error("Failed to parse boolean assignment %s.", assignment);
- return -EINVAL;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse boolean assignment %s.", assignment);
r = sd_bus_message_append(m, "v", "b", r);
@@ -1493,10 +1551,33 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
"UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath",
"StandardInput", "StandardOutput", "StandardError",
"Description", "Slice", "Type", "WorkingDirectory",
- "RootDirectory"))
+ "RootDirectory", "SyslogIdentifier", "ProtectSystem",
+ "ProtectHome"))
r = sd_bus_message_append(m, "v", "s", eq);
- else if (streq(field, "DeviceAllow")) {
+ else if (streq(field, "SyslogLevel")) {
+ int level;
+
+ level = log_level_from_string(eq);
+ if (level < 0) {
+ log_error("Failed to parse %s value %s.", field, eq);
+ return -EINVAL;
+ }
+
+ r = sd_bus_message_append(m, "v", "i", level);
+
+ } else if (streq(field, "SyslogFacility")) {
+ int facility;
+
+ facility = log_facility_unshifted_from_string(eq);
+ if (facility < 0) {
+ log_error("Failed to parse %s value %s.", field, eq);
+ return -EINVAL;
+ }
+
+ r = sd_bus_message_append(m, "v", "i", facility);
+
+ } else if (streq(field, "DeviceAllow")) {
if (isempty(eq))
r = sd_bus_message_append(m, "v", "a(ss)", 0);
@@ -1607,9 +1688,52 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
r = sd_bus_message_append(m, "v", "i", i);
- } else if (streq(field, "Environment")) {
+ } else if (STR_IN_SET(field, "Environment", "PassEnvironment")) {
+ const char *p;
+
+ r = sd_bus_message_open_container(m, 'v', "as");
+ 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);
+
+ p = eq;
+
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
+
+ r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
+ if (r < 0) {
+ log_error("Failed to parse Environment value %s", eq);
+ return -EINVAL;
+ }
+ if (r == 0)
+ break;
+
+ if (streq(field, "Environment")) {
+ if (!env_assignment_is_valid(word)) {
+ log_error("Invalid environment assignment: %s", word);
+ return -EINVAL;
+ }
+ } else { /* PassEnvironment */
+ if (!env_name_is_valid(word)) {
+ log_error("Invalid environment variable name: %s", word);
+ return -EINVAL;
+ }
+ }
+
+ r = sd_bus_message_append_basic(m, 's', word);
+ if (r < 0)
+ return bus_log_create_error(r);
+ }
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
- r = sd_bus_message_append(m, "v", "as", 1, eq);
+ r = sd_bus_message_close_container(m);
} else if (streq(field, "KillSignal")) {
int sig;
@@ -1632,6 +1756,113 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
}
r = sd_bus_message_append(m, "v", "t", u);
+ } else if (streq(field, "TimerSlackNSec")) {
+ nsec_t n;
+
+ r = parse_nsec(eq, &n);
+ if (r < 0) {
+ log_error("Failed to parse %s value %s", field, eq);
+ return -EINVAL;
+ }
+
+ r = sd_bus_message_append(m, "v", "t", n);
+ } else if (streq(field, "OOMScoreAdjust")) {
+ int oa;
+
+ r = safe_atoi(eq, &oa);
+ if (r < 0) {
+ log_error("Failed to parse %s value %s", field, eq);
+ return -EINVAL;
+ }
+
+ if (!oom_score_adjust_is_valid(oa)) {
+ log_error("OOM score adjust value out of range");
+ return -EINVAL;
+ }
+
+ r = sd_bus_message_append(m, "v", "i", oa);
+ } else if (STR_IN_SET(field, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories")) {
+ const char *p;
+
+ r = sd_bus_message_open_container(m, 'v', "as");
+ 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);
+
+ p = eq;
+
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
+ int offset;
+
+ r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
+ if (r < 0) {
+ log_error("Failed to parse %s value %s", field, eq);
+ return -EINVAL;
+ }
+ if (r == 0)
+ break;
+
+ if (!utf8_is_valid(word)) {
+ log_error("Failed to parse %s value %s", field, eq);
+ return -EINVAL;
+ }
+
+ offset = word[0] == '-';
+ if (!path_is_absolute(word + offset)) {
+ log_error("Failed to parse %s value %s", field, eq);
+ return -EINVAL;
+ }
+
+ path_kill_slashes(word + offset);
+
+ r = sd_bus_message_append_basic(m, 's', word);
+ if (r < 0)
+ return bus_log_create_error(r);
+ }
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_close_container(m);
+
+ } else if (streq(field, "RuntimeDirectory")) {
+ const char *p;
+
+ r = sd_bus_message_open_container(m, 'v', "as");
+ 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);
+
+ p = eq;
+
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
+
+ r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse %s value %s", field, eq);
+
+ if (r == 0)
+ break;
+
+ r = sd_bus_message_append_basic(m, 's', word);
+ if (r < 0)
+ return bus_log_create_error(r);
+ }
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_close_container(m);
} else {
log_error("Unknown assignment %s.", assignment);
@@ -1810,13 +2041,21 @@ static const struct {
{ "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;
+static void log_job_error_with_service_result(const char* service, const char *result, const char *extra_args) {
+ _cleanup_free_ char *service_shell_quoted = NULL, *systemctl_extra_args = NULL;
assert(service);
service_shell_quoted = shell_maybe_quote(service);
+ systemctl_extra_args = strjoin("systemctl ", extra_args, " ", NULL);
+ if (!systemctl_extra_args) {
+ log_oom();
+ return;
+ }
+
+ systemctl_extra_args = strstrip(systemctl_extra_args);
+
if (!isempty(result)) {
unsigned i;
@@ -1825,27 +2064,30 @@ static void log_job_error_with_service_result(const char* service, const char *r
break;
if (i < ELEMENTSOF(explanations)) {
- log_error("Job for %s failed because %s. See \"systemctl status %s\" and \"journalctl -xe\" for details.\n",
+ log_error("Job for %s failed because %s. See \"%s status %s\" and \"journalctl -xe\" for details.\n",
service,
explanations[i].explanation,
+ systemctl_extra_args,
strna(service_shell_quoted));
goto finish;
}
}
- log_error("Job for %s failed. See \"systemctl status %s\" and \"journalctl -xe\" for details.\n",
+ log_error("Job for %s failed. See \"%s status %s\" and \"journalctl -xe\" for details.\n",
service,
+ systemctl_extra_args,
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.",
+ log_info("To force a start use \"%1$s reset-failed %2$s\" followed by \"%1$s start %2$s\" again.",
+ systemctl_extra_args,
strna(service_shell_quoted));
}
-static int check_wait_response(BusWaitForJobs *d, bool quiet) {
+static int check_wait_response(BusWaitForJobs *d, bool quiet, const char *extra_args) {
int r = 0;
assert(d->result);
@@ -1872,7 +2114,7 @@ static int check_wait_response(BusWaitForJobs *d, bool quiet) {
if (q < 0)
log_debug_errno(q, "Failed to get Result property of service %s: %m", d->name);
- log_job_error_with_service_result(d->name, result);
+ log_job_error_with_service_result(d->name, result, extra_args);
} else
log_error("Job failed. See \"journalctl -xe\" for details.");
}
@@ -1896,7 +2138,7 @@ static int check_wait_response(BusWaitForJobs *d, bool quiet) {
return r;
}
-int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet) {
+int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char *extra_args) {
int r = 0;
assert(d);
@@ -1909,7 +2151,7 @@ int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet) {
return log_error_errno(q, "Failed to wait for response: %m");
if (d->result) {
- q = check_wait_response(d, quiet);
+ q = check_wait_response(d, quiet, extra_args);
/* Return the first error as it is most likely to be
* meaningful. */
if (q < 0 && r == 0)
@@ -1944,7 +2186,7 @@ int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) {
if (r < 0)
return log_oom();
- return bus_wait_for_jobs(d, quiet);
+ return bus_wait_for_jobs(d, quiet, NULL);
}
int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes) {
@@ -2137,3 +2379,47 @@ bool is_kdbus_available(void) {
return ioctl(fd, KDBUS_CMD_BUS_MAKE, &cmd) >= 0;
}
+
+int bus_property_get_rlimit(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ struct rlimit *rl;
+ uint64_t u;
+ rlim_t x;
+ const char *is_soft;
+
+ assert(bus);
+ assert(reply);
+ assert(userdata);
+
+ is_soft = endswith(property, "Soft");
+ rl = *(struct rlimit**) userdata;
+ if (rl)
+ x = is_soft ? rl->rlim_cur : rl->rlim_max;
+ else {
+ struct rlimit buf = {};
+ int z;
+ const char *s;
+
+ s = is_soft ? strndupa(property, is_soft - property) : property;
+
+ z = rlimit_from_string(strstr(s, "Limit"));
+ assert(z >= 0);
+
+ getrlimit(z, &buf);
+ x = is_soft ? buf.rlim_cur : buf.rlim_max;
+ }
+
+ /* rlim_t might have different sizes, let's map
+ * RLIMIT_INFINITY to (uint64_t) -1, so that it is the same on
+ * all archs */
+ u = x == RLIM_INFINITY ? (uint64_t) -1 : (uint64_t) x;
+
+ return sd_bus_message_append(reply, "t", u);
+}
diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h
index d2b2d701ce..18fc827754 100644
--- a/src/shared/bus-util.h
+++ b/src/shared/bus-util.h
@@ -21,10 +21,19 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "sd-event.h"
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "sd-bus-vtable.h"
#include "sd-bus.h"
+#include "sd-event.h"
+
#include "hashmap.h"
#include "install.h"
+#include "macro.h"
+#include "string-util.h"
#include "time-util.h"
typedef enum BusTransport {
@@ -65,11 +74,11 @@ int bus_test_polkit(sd_bus_message *call, int capability, const char *action, co
int bus_verify_polkit_async(sd_bus_message *call, int capability, const char *action, const char **details, 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);
-int bus_open_user_systemd(sd_bus **_bus);
+int bus_connect_system_systemd(sd_bus **_bus);
+int bus_connect_user_systemd(sd_bus **_bus);
-int bus_open_transport(BusTransport transport, const char *host, bool user, sd_bus **bus);
-int bus_open_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus);
+int bus_connect_transport(BusTransport transport, const char *host, bool user, sd_bus **bus);
+int bus_connect_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus);
int bus_print_property(const char *name, sd_bus_message *property, bool all);
int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, char **filter, bool all);
@@ -135,21 +144,6 @@ typedef struct UnitInfo {
int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u);
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_bus*, sd_bus_unref);
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_bus*, sd_bus_flush_close_unref);
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_bus_slot*, sd_bus_slot_unref);
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_bus_message*, sd_bus_message_unref);
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_bus_creds*, sd_bus_creds_unref);
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_bus_track*, sd_bus_track_unref);
-
-#define _cleanup_bus_unref_ _cleanup_(sd_bus_unrefp)
-#define _cleanup_bus_flush_close_unref_ _cleanup_(sd_bus_flush_close_unrefp)
-#define _cleanup_bus_slot_unref_ _cleanup_(sd_bus_slot_unrefp)
-#define _cleanup_bus_message_unref_ _cleanup_(sd_bus_message_unrefp)
-#define _cleanup_bus_creds_unref_ _cleanup_(sd_bus_creds_unrefp)
-#define _cleanup_bus_track_unref_ _cleanup_(sd_bus_slot_unrefp)
-#define _cleanup_bus_error_free_ _cleanup_(sd_bus_error_free)
-
#define BUS_DEFINE_PROPERTY_GET_ENUM(function, name, type) \
int function(sd_bus *bus, \
const char *path, \
@@ -188,7 +182,7 @@ typedef struct BusWaitForJobs BusWaitForJobs;
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(BusWaitForJobs *d, bool quiet, const char *extra_args);
int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet);
DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForJobs*, bus_wait_for_jobs_free);
@@ -200,3 +194,5 @@ int bus_path_decode_unique(const char *path, const char *prefix, char **ret_send
bool is_kdbus_wanted(void);
bool is_kdbus_available(void);
+
+int bus_property_get_rlimit(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
diff --git a/src/shared/cgroup-show.c b/src/shared/cgroup-show.c
index 31b4f6c684..d256b5a7cc 100644
--- a/src/shared/cgroup-show.c
+++ b/src/shared/cgroup-show.c
@@ -19,18 +19,24 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
-#include <string.h>
#include <dirent.h>
#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
-#include "util.h"
+#include "alloc-util.h"
+#include "cgroup-show.h"
+#include "cgroup-util.h"
+#include "fd-util.h"
#include "formats-util.h"
-#include "process-util.h"
+#include "locale-util.h"
#include "macro.h"
+#include "output-mode.h"
#include "path-util.h"
-#include "cgroup-util.h"
-#include "cgroup-show.h"
+#include "process-util.h"
+#include "string-util.h"
#include "terminal-util.h"
static int compare(const void *a, const void *b) {
diff --git a/src/shared/cgroup-show.h b/src/shared/cgroup-show.h
index aa832454b5..24b758658d 100644
--- a/src/shared/cgroup-show.h
+++ b/src/shared/cgroup-show.h
@@ -23,7 +23,9 @@
#include <stdbool.h>
#include <sys/types.h>
+
#include "logs-show.h"
+#include "output-mode.h"
int show_cgroup_by_path(const char *path, const char *prefix, unsigned columns, bool kernel_threads, OutputFlags flags);
int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned columns, bool kernel_threads, OutputFlags flags);
diff --git a/src/shared/clean-ipc.c b/src/shared/clean-ipc.c
index d1cdb151b2..2c494d3a31 100644
--- a/src/shared/clean-ipc.c
+++ b/src/shared/clean-ipc.c
@@ -19,19 +19,30 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <mqueue.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
#include <sys/ipc.h>
-#include <sys/shm.h>
-#include <sys/sem.h>
#include <sys/msg.h>
+#include <sys/sem.h>
+#include <sys/shm.h>
#include <sys/stat.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <mqueue.h>
+#include <unistd.h>
-#include "util.h"
+#include "clean-ipc.h"
+#include "dirent-util.h"
+#include "fd-util.h"
+#include "fileio.h"
#include "formats-util.h"
+#include "log.h"
+#include "macro.h"
+#include "string-util.h"
#include "strv.h"
-#include "clean-ipc.h"
static int clean_sysvipc_shm(uid_t delete_uid) {
_cleanup_fclose_ FILE *f = NULL;
@@ -44,8 +55,7 @@ static int clean_sysvipc_shm(uid_t delete_uid) {
if (errno == ENOENT)
return 0;
- log_warning_errno(errno, "Failed to open /proc/sysvipc/shm: %m");
- return -errno;
+ return log_warning_errno(errno, "Failed to open /proc/sysvipc/shm: %m");
}
FOREACH_LINE(line, f, goto fail) {
@@ -87,8 +97,7 @@ static int clean_sysvipc_shm(uid_t delete_uid) {
return ret;
fail:
- log_warning_errno(errno, "Failed to read /proc/sysvipc/shm: %m");
- return -errno;
+ return log_warning_errno(errno, "Failed to read /proc/sysvipc/shm: %m");
}
static int clean_sysvipc_sem(uid_t delete_uid) {
@@ -102,8 +111,7 @@ static int clean_sysvipc_sem(uid_t delete_uid) {
if (errno == ENOENT)
return 0;
- log_warning_errno(errno, "Failed to open /proc/sysvipc/sem: %m");
- return -errno;
+ return log_warning_errno(errno, "Failed to open /proc/sysvipc/sem: %m");
}
FOREACH_LINE(line, f, goto fail) {
@@ -140,8 +148,7 @@ static int clean_sysvipc_sem(uid_t delete_uid) {
return ret;
fail:
- log_warning_errno(errno, "Failed to read /proc/sysvipc/sem: %m");
- return -errno;
+ return log_warning_errno(errno, "Failed to read /proc/sysvipc/sem: %m");
}
static int clean_sysvipc_msg(uid_t delete_uid) {
@@ -155,8 +162,7 @@ static int clean_sysvipc_msg(uid_t delete_uid) {
if (errno == ENOENT)
return 0;
- log_warning_errno(errno, "Failed to open /proc/sysvipc/msg: %m");
- return -errno;
+ return log_warning_errno(errno, "Failed to open /proc/sysvipc/msg: %m");
}
FOREACH_LINE(line, f, goto fail) {
@@ -194,8 +200,7 @@ static int clean_sysvipc_msg(uid_t delete_uid) {
return ret;
fail:
- log_warning_errno(errno, "Failed to read /proc/sysvipc/msg: %m");
- return -errno;
+ return log_warning_errno(errno, "Failed to read /proc/sysvipc/msg: %m");
}
static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
@@ -273,8 +278,7 @@ static int clean_posix_shm(uid_t uid) {
if (errno == ENOENT)
return 0;
- log_warning_errno(errno, "Failed to open /dev/shm: %m");
- return -errno;
+ return log_warning_errno(errno, "Failed to open /dev/shm: %m");
}
return clean_posix_shm_internal(dir, uid);
@@ -290,8 +294,7 @@ static int clean_posix_mq(uid_t uid) {
if (errno == ENOENT)
return 0;
- log_warning_errno(errno, "Failed to open /dev/mqueue: %m");
- return -errno;
+ return log_warning_errno(errno, "Failed to open /dev/mqueue: %m");
}
FOREACH_DIRENT(de, dir, goto fail) {
@@ -330,8 +333,7 @@ static int clean_posix_mq(uid_t uid) {
return ret;
fail:
- log_warning_errno(errno, "Failed to read /dev/mqueue: %m");
- return -errno;
+ return log_warning_errno(errno, "Failed to read /dev/mqueue: %m");
}
int clean_ipc(uid_t uid) {
diff --git a/src/shared/condition.c b/src/shared/condition.c
index 1d7dd49e04..dedaf2291f 100644
--- a/src/shared/condition.c
+++ b/src/shared/condition.c
@@ -19,25 +19,42 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
#include <errno.h>
+#include <fcntl.h>
+#include <fnmatch.h>
+#include <limits.h>
+#include <stdlib.h>
#include <string.h>
+#include <sys/stat.h>
+#include <time.h>
#include <unistd.h>
-#include <fnmatch.h>
#include "sd-id128.h"
-#include "util.h"
-#include "virt.h"
-#include "path-util.h"
-#include "architecture.h"
-#include "smack-util.h"
+
+#include "alloc-util.h"
#include "apparmor-util.h"
-#include "ima-util.h"
-#include "selinux-util.h"
-#include "audit.h"
+#include "architecture.h"
+#include "audit-util.h"
#include "cap-list.h"
-#include "hostname-util.h"
#include "condition.h"
+#include "extract-word.h"
+#include "fd-util.h"
+#include "glob-util.h"
+#include "hostname-util.h"
+#include "ima-util.h"
+#include "list.h"
+#include "macro.h"
+#include "mount-util.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "proc-cmdline.h"
+#include "selinux-util.h"
+#include "smack-util.h"
+#include "stat-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "util.h"
+#include "virt.h"
Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
Condition *c;
@@ -220,7 +237,7 @@ static int condition_test_security(Condition *c) {
assert(c->type == CONDITION_SECURITY);
if (streq(c->parameter, "selinux"))
- return mac_selinux_use();
+ return mac_selinux_have();
if (streq(c->parameter, "smack"))
return mac_smack_use();
if (streq(c->parameter, "apparmor"))
diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c
index 946eac6823..2aae49fbce 100644
--- a/src/shared/conf-parser.c
+++ b/src/shared/conf-parser.c
@@ -19,21 +19,31 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <string.h>
-#include <stdio.h>
#include <errno.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
-#include "sd-messages.h"
+#include "alloc-util.h"
#include "conf-files.h"
-#include "util.h"
-#include "macro.h"
-#include "strv.h"
+#include "conf-parser.h"
+#include "extract-word.h"
+#include "fd-util.h"
+#include "fs-util.h"
#include "log.h"
-#include "utf8.h"
+#include "macro.h"
+#include "parse-util.h"
#include "path-util.h"
+#include "process-util.h"
#include "signal-util.h"
-#include "conf-parser.h"
+#include "string-util.h"
+#include "strv.h"
+#include "syslog-util.h"
+#include "time-util.h"
+#include "utf8.h"
int config_item_table_lookup(
const void *table,
@@ -147,8 +157,7 @@ static int next_assignment(const char *unit,
/* Warn about unknown non-extension fields. */
if (!relaxed && !startswith(lvalue, "X-"))
- log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
- "Unknown lvalue '%s' in section '%s'", lvalue, section);
+ log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown lvalue '%s' in section '%s'", lvalue, section);
return 0;
}
@@ -196,8 +205,7 @@ static int parse_line(const char* unit,
* Support for them should be eventually removed. */
if (!allow_include) {
- log_syntax(unit, LOG_ERR, filename, line, EBADMSG,
- ".include not allowed here. Ignoring.");
+ log_syntax(unit, LOG_ERR, filename, line, 0, ".include not allowed here. Ignoring.");
return 0;
}
@@ -216,8 +224,7 @@ static int parse_line(const char* unit,
assert(k > 0);
if (l[k-1] != ']') {
- log_syntax(unit, LOG_ERR, filename, line, EBADMSG,
- "Invalid section header '%s'", l);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid section header '%s'", l);
return -EBADMSG;
}
@@ -228,8 +235,7 @@ static int parse_line(const char* unit,
if (sections && !nulstr_contains(sections, n)) {
if (!relaxed && !startswith(n, "X-"))
- log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
- "Unknown section '%s'. Ignoring.", n);
+ log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown section '%s'. Ignoring.", n);
free(n);
*section = mfree(*section);
@@ -248,16 +254,15 @@ static int parse_line(const char* unit,
if (sections && !*section) {
if (!relaxed && !*section_ignored)
- log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
- "Assignment outside of section. Ignoring.");
+ log_syntax(unit, LOG_WARNING, filename, line, 0, "Assignment outside of section. Ignoring.");
return 0;
}
e = strchr(l, '=');
if (!e) {
- log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Missing '='.");
- return -EBADMSG;
+ log_syntax(unit, LOG_WARNING, filename, line, 0, "Missing '='.");
+ return -EINVAL;
}
*e = 0;
@@ -420,16 +425,17 @@ int config_parse_many(const char *conf_file,
}
#define DEFINE_PARSER(type, vartype, conv_func) \
- int config_parse_##type(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_##type( \
+ 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) { \
\
vartype *i = data; \
int r; \
@@ -441,21 +447,23 @@ int config_parse_many(const char *conf_file,
\
r = conv_func(rvalue, i); \
if (r < 0) \
- log_syntax(unit, LOG_ERR, filename, line, -r, \
+ log_syntax(unit, LOG_ERR, filename, line, r, \
"Failed to parse %s value, ignoring: %s", \
#type, rvalue); \
\
return 0; \
- }
-
-DEFINE_PARSER(int, int, safe_atoi)
-DEFINE_PARSER(long, long, safe_atoli)
-DEFINE_PARSER(uint32, uint32_t, safe_atou32)
-DEFINE_PARSER(uint64, uint64_t, safe_atou64)
-DEFINE_PARSER(unsigned, unsigned, safe_atou)
-DEFINE_PARSER(double, double, safe_atod)
-DEFINE_PARSER(nsec, nsec_t, parse_nsec)
-DEFINE_PARSER(sec, usec_t, parse_sec)
+ } \
+ struct __useless_struct_to_allow_trailing_semicolon__
+
+DEFINE_PARSER(int, int, safe_atoi);
+DEFINE_PARSER(long, long, safe_atoli);
+DEFINE_PARSER(uint32, uint32_t, safe_atou32);
+DEFINE_PARSER(uint64, uint64_t, safe_atou64);
+DEFINE_PARSER(unsigned, unsigned, safe_atou);
+DEFINE_PARSER(double, double, safe_atod);
+DEFINE_PARSER(nsec, nsec_t, parse_nsec);
+DEFINE_PARSER(sec, usec_t, parse_sec);
+DEFINE_PARSER(mode, mode_t, parse_mode);
int config_parse_iec_size(const char* unit,
const char *filename,
@@ -479,7 +487,7 @@ int config_parse_iec_size(const char* unit,
r = parse_size(rvalue, 1024, &v);
if (r < 0 || (uint64_t) (size_t) v != v) {
- log_syntax(unit, LOG_ERR, filename, line, r < 0 ? r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
return 0;
}
@@ -509,7 +517,7 @@ int config_parse_si_size(const char* unit,
r = parse_size(rvalue, 1000, &v);
if (r < 0 || (uint64_t) (size_t) v != v) {
- log_syntax(unit, LOG_ERR, filename, line, r < 0 ? r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
return 0;
}
@@ -564,8 +572,7 @@ int config_parse_bool(const char* unit,
k = parse_boolean(rvalue);
if (k < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -k,
- "Failed to parse boolean value, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
return 0;
}
@@ -626,7 +633,7 @@ int config_parse_string(
assert(data);
if (!utf8_is_valid(rvalue)) {
- log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
+ log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
return 0;
}
@@ -664,12 +671,12 @@ int config_parse_path(
assert(data);
if (!utf8_is_valid(rvalue)) {
- log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
+ log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
return 0;
}
if (!path_is_absolute(rvalue)) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Not an absolute path, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute path, ignoring: %s", rvalue);
return 0;
}
@@ -697,9 +704,6 @@ int config_parse_strv(const char *unit,
void *userdata) {
char ***sv = data;
- const char *word, *state;
- size_t l;
- int r;
assert(filename);
assert(lvalue);
@@ -722,53 +726,28 @@ int config_parse_strv(const char *unit,
return 0;
}
- FOREACH_WORD_QUOTED(word, l, rvalue, state) {
- char *n;
-
- n = strndup(word, l);
- if (!n)
+ for (;;) {
+ char *word = NULL;
+ int r;
+ r = extract_first_word(&rvalue, &word, WHITESPACE, EXTRACT_QUOTES);
+ if (r == 0)
+ break;
+ if (r == -ENOMEM)
return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
+ break;
+ }
- if (!utf8_is_valid(n)) {
- log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
- free(n);
+ if (!utf8_is_valid(word)) {
+ log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
+ free(word);
continue;
}
-
- r = strv_consume(sv, n);
+ r = strv_consume(sv, word);
if (r < 0)
return log_oom();
}
- if (!isempty(state))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Trailing garbage, ignoring.");
-
- return 0;
-}
-
-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) {
-
- mode_t *m = data;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if (parse_mode(rvalue, m) < 0) {
- log_syntax(unit, LOG_ERR, filename, line, errno, "Failed to parse mode value, ignoring: %s", rvalue);
- return 0;
- }
return 0;
}
@@ -795,7 +774,7 @@ int config_parse_log_facility(
x = log_facility_unshifted_from_string(rvalue);
if (x < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse log facility, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log facility, ignoring: %s", rvalue);
return 0;
}
@@ -826,7 +805,7 @@ int config_parse_log_level(
x = log_level_from_string(rvalue);
if (x < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse log level, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log level, ignoring: %s", rvalue);
return 0;
}
@@ -855,7 +834,7 @@ int config_parse_signal(
r = signal_from_string_try_harder(rvalue);
if (r <= 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse signal name, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse signal name, ignoring: %s", rvalue);
return 0;
}
@@ -884,7 +863,7 @@ int config_parse_personality(
p = personality_from_string(rvalue);
if (p == PERSONALITY_INVALID) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse personality, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse personality, ignoring: %s", rvalue);
return 0;
}
diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h
index 4efed138c9..027ed209d9 100644
--- a/src/shared/conf-parser.h
+++ b/src/shared/conf-parser.h
@@ -21,9 +21,14 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
+#include <errno.h>
#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <syslog.h>
+#include "alloc-util.h"
+#include "log.h"
#include "macro.h"
/* An abstract parser for simple, line based, shallow configuration
@@ -123,13 +128,6 @@ int config_parse_log_level(const char *unit, const char *filename, unsigned line
int config_parse_signal(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_personality(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);
-#define log_invalid_utf8(unit, level, config_file, config_line, error, rvalue) \
- do { \
- _cleanup_free_ char *_p = utf8_escape_invalid(rvalue); \
- log_syntax(unit, level, config_file, config_line, error, \
- "String is not UTF-8 clean, ignoring assignment: %s", strna(_p)); \
- } while(false)
-
#define DEFINE_CONFIG_PARSE_ENUM(function,name,type,msg) \
int function(const char *unit, \
const char *filename, \
diff --git a/src/shared/dev-setup.c b/src/shared/dev-setup.c
index 25ad918b85..ff583faa6e 100644
--- a/src/shared/dev-setup.c
+++ b/src/shared/dev-setup.c
@@ -23,10 +23,13 @@
#include <stdlib.h>
#include <unistd.h>
-#include "util.h"
+#include "alloc-util.h"
+#include "dev-setup.h"
#include "label.h"
+#include "log.h"
#include "path-util.h"
-#include "dev-setup.h"
+#include "user-util.h"
+#include "util.h"
int dev_setup(const char *prefix, uid_t uid, gid_t gid) {
static const char symlinks[] =
diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c
index 6dc04d51e4..3ad409fc29 100644
--- a/src/shared/dns-domain.c
+++ b/src/shared/dns-domain.c
@@ -24,7 +24,22 @@
#include <stringprep.h>
#endif
+#include <endian.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include "alloc-util.h"
#include "dns-domain.h"
+#include "hashmap.h"
+#include "hexdecoct.h"
+#include "in-addr-util.h"
+#include "macro.h"
+#include "parse-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "utf8.h"
int dns_label_unescape(const char **name, char *dest, size_t sz) {
const char *n;
@@ -33,7 +48,6 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
assert(name);
assert(*name);
- assert(dest);
n = *name;
d = dest;
@@ -47,12 +61,12 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
if (*n == 0)
break;
- if (sz <= 0)
- return -ENOSPC;
-
if (r >= DNS_LABEL_MAX)
return -EINVAL;
+ if (sz <= 0)
+ return -ENOBUFS;
+
if (*n == '\\') {
/* Escaped character */
@@ -64,9 +78,12 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
else if (*n == '\\' || *n == '.') {
/* Escaped backslash or dot */
- *(d++) = *(n++);
+
+ if (d)
+ *(d++) = *n;
sz--;
r++;
+ n++;
} else if (n[0] >= '0' && n[0] <= '9') {
unsigned k;
@@ -81,11 +98,17 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
((unsigned) (n[1] - '0') * 10) +
((unsigned) (n[2] - '0'));
- /* Don't allow CC characters or anything that doesn't fit in 8bit */
- if (k < ' ' || k > 255 || k == 127)
+ /* Don't allow anything that doesn't
+ * fit in 8bit. Note that we do allow
+ * control characters, as some servers
+ * (e.g. cloudflare) are happy to
+ * generate labels with them
+ * inside. */
+ if (k > 255)
return -EINVAL;
- *(d++) = (char) k;
+ if (d)
+ *(d++) = (char) k;
sz--;
r++;
@@ -96,9 +119,12 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
} else if ((uint8_t) *n >= (uint8_t) ' ' && *n != 127) {
/* Normal character */
- *(d++) = *(n++);
+
+ if (d)
+ *(d++) = *n;
sz--;
r++;
+ n++;
} else
return -EINVAL;
}
@@ -107,7 +133,7 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
if (r == 0 && *n)
return -EINVAL;
- if (sz >= 1)
+ if (sz >= 1 && d)
*d = 0;
*name = n;
@@ -133,20 +159,24 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha
return 0;
}
- assert(**label_terminal == '.' || **label_terminal == 0);
+ terminal = *label_terminal;
+ assert(*terminal == '.' || *terminal == 0);
- /* skip current terminal character */
- terminal = *label_terminal - 1;
+ /* Skip current terminal character (and accept domain names ending it ".") */
+ if (*terminal == 0)
+ terminal--;
+ if (terminal >= name && *terminal == '.')
+ terminal--;
- /* point name to the last label, and terminal to the preceding terminal symbol (or make it a NULL pointer) */
+ /* Point name to the last label, and terminal to the preceding terminal symbol (or make it a NULL pointer) */
for (;;) {
if (terminal < name) {
- /* reached the first label, so indicate that there are no more */
+ /* Reached the first label, so indicate that there are no more */
terminal = NULL;
break;
}
- /* find the start of the last label */
+ /* Find the start of the last label */
if (*terminal == '.') {
const char *y;
unsigned slashes = 0;
@@ -155,7 +185,7 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha
slashes ++;
if (slashes % 2 == 0) {
- /* the '.' was not escaped */
+ /* The '.' was not escaped */
name = terminal + 1;
break;
} else {
@@ -176,30 +206,36 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha
return r;
}
-int dns_label_escape(const char *p, size_t l, char **ret) {
- _cleanup_free_ char *s = NULL;
+int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) {
char *q;
- int r;
- assert(p);
- assert(ret);
+ /* DNS labels must be between 1 and 63 characters long. A
+ * zero-length label does not exist. See RFC 2182, Section
+ * 11. */
- if (l > DNS_LABEL_MAX)
+ if (l <= 0 || l > DNS_LABEL_MAX)
return -EINVAL;
+ if (sz < 1)
+ return -ENOBUFS;
- s = malloc(l * 4 + 1);
- if (!s)
- return -ENOMEM;
+ assert(p);
+ assert(dest);
- q = s;
+ q = dest;
while (l > 0) {
if (*p == '.' || *p == '\\') {
/* Dot or backslash */
+
+ if (sz < 3)
+ return -ENOBUFS;
+
*(q++) = '\\';
*(q++) = *p;
+ sz -= 2;
+
} else if (*p == '_' ||
*p == '-' ||
(*p >= '0' && *p <= '9') ||
@@ -207,25 +243,55 @@ int dns_label_escape(const char *p, size_t l, char **ret) {
(*p >= 'A' && *p <= 'Z')) {
/* Proper character */
+
+ if (sz < 2)
+ return -ENOBUFS;
+
*(q++) = *p;
- } else if ((uint8_t) *p >= (uint8_t) ' ' && *p != 127) {
+ sz -= 1;
+
+ } else {
/* Everything else */
+
+ if (sz < 5)
+ return -ENOBUFS;
+
*(q++) = '\\';
*(q++) = '0' + (char) ((uint8_t) *p / 100);
*(q++) = '0' + (char) (((uint8_t) *p / 10) % 10);
*(q++) = '0' + (char) ((uint8_t) *p % 10);
- } else
- return -EINVAL;
+ sz -= 4;
+ }
p++;
l--;
}
*q = 0;
+ return (int) (q - dest);
+}
+
+int dns_label_escape_new(const char *p, size_t l, char **ret) {
+ _cleanup_free_ char *s = NULL;
+ int r;
+
+ assert(p);
+ assert(ret);
+
+ if (l <= 0 || l > DNS_LABEL_MAX)
+ return -EINVAL;
+
+ s = new(char, DNS_LABEL_ESCAPED_MAX);
+ if (!s)
+ return -ENOMEM;
+
+ r = dns_label_escape(p, l, s, DNS_LABEL_ESCAPED_MAX);
+ if (r < 0)
+ return r;
+
*ret = s;
- r = q - s;
s = NULL;
return r;
@@ -234,32 +300,52 @@ int dns_label_escape(const char *p, size_t l, char **ret) {
int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) {
#ifdef HAVE_LIBIDN
_cleanup_free_ uint32_t *input = NULL;
- size_t input_size;
+ size_t input_size, l;
const char *p;
bool contains_8bit = false;
+ char buffer[DNS_LABEL_MAX+1];
assert(encoded);
assert(decoded);
- assert(decoded_max >= DNS_LABEL_MAX);
+
+ /* Converts an U-label into an A-label */
if (encoded_size <= 0)
- return 0;
+ return -EINVAL;
for (p = encoded; p < encoded + encoded_size; p++)
if ((uint8_t) *p > 127)
contains_8bit = true;
- if (!contains_8bit)
+ if (!contains_8bit) {
+ if (encoded_size > DNS_LABEL_MAX)
+ return -EINVAL;
+
return 0;
+ }
input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size);
if (!input)
return -ENOMEM;
- if (idna_to_ascii_4i(input, input_size, decoded, 0) != 0)
+ if (idna_to_ascii_4i(input, input_size, buffer, 0) != 0)
+ return -EINVAL;
+
+ l = strlen(buffer);
+
+ /* Verify that the the result is not longer than one DNS label. */
+ if (l <= 0 || l > DNS_LABEL_MAX)
return -EINVAL;
+ if (l > decoded_max)
+ return -ENOBUFS;
+
+ memcpy(decoded, buffer, l);
+
+ /* If there's room, append a trailing NUL byte, but only then */
+ if (decoded_max > l)
+ decoded[l] = 0;
- return strlen(decoded);
+ return (int) l;
#else
return 0;
#endif
@@ -273,11 +359,14 @@ int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded,
uint32_t *output = NULL;
size_t w;
- /* To be invoked after unescaping */
+ /* To be invoked after unescaping. Converts an A-label into an U-label. */
assert(encoded);
assert(decoded);
+ if (encoded_size <= 0 || encoded_size > DNS_LABEL_MAX)
+ return -EINVAL;
+
if (encoded_size < sizeof(IDNA_ACE_PREFIX)-1)
return 0;
@@ -297,11 +386,16 @@ int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded,
if (!result)
return -ENOMEM;
if (w <= 0)
- return 0;
- if (w+1 > decoded_max)
return -EINVAL;
+ if (w > decoded_max)
+ return -ENOBUFS;
+
+ memcpy(decoded, result, w);
+
+ /* Append trailing NUL byte if there's space, but only then. */
+ if (decoded_max > w)
+ decoded[w] = 0;
- memcpy(decoded, result, w+1);
return w;
#else
return 0;
@@ -318,9 +412,7 @@ int dns_name_concat(const char *a, const char *b, char **_ret) {
assert(a);
for (;;) {
- _cleanup_free_ char *t = NULL;
char label[DNS_LABEL_MAX];
- int k;
r = dns_label_unescape(&p, label, sizeof(label));
if (r < 0)
@@ -339,32 +431,33 @@ int dns_name_concat(const char *a, const char *b, char **_ret) {
break;
}
- k = dns_label_undo_idna(label, r, label, sizeof(label));
- if (k < 0)
- return k;
- if (k > 0)
- r = k;
-
- r = dns_label_escape(label, r, &t);
- if (r < 0)
- return r;
-
if (_ret) {
- if (!GREEDY_REALLOC(ret, allocated, n + !first + strlen(t) + 1))
+ if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
return -ENOMEM;
+ r = dns_label_escape(label, r, ret + n + !first, DNS_LABEL_ESCAPED_MAX);
+ if (r < 0)
+ return r;
+
if (!first)
- ret[n++] = '.';
- else
- first = false;
+ ret[n] = '.';
+ } else {
+ char escaped[DNS_LABEL_ESCAPED_MAX];
- memcpy(ret + n, t, r);
+ r = dns_label_escape(label, r, escaped, sizeof(escaped));
+ if (r < 0)
+ return r;
}
+ if (!first)
+ n++;
+ else
+ first = false;
+
n += r;
}
- if (n > DNS_NAME_MAX)
+ if (n > DNS_HOSTNAME_MAX)
return -EINVAL;
if (_ret) {
@@ -379,39 +472,33 @@ int dns_name_concat(const char *a, const char *b, char **_ret) {
return 0;
}
-unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_SIZE]) {
+void dns_name_hash_func(const void *s, struct siphash *state) {
const char *p = s;
- unsigned long ul = hash_key[0];
int r;
assert(p);
- while (*p) {
+ for (;;) {
char label[DNS_LABEL_MAX+1];
- int k;
r = dns_label_unescape(&p, label, sizeof(label));
if (r < 0)
break;
-
- k = dns_label_undo_idna(label, r, label, sizeof(label));
- if (k < 0)
+ if (r == 0)
break;
- if (k > 0)
- r = k;
-
- label[r] = 0;
- ascii_strlower(label);
- ul = ul * hash_key[1] + ul + string_hash_func(label, hash_key);
+ ascii_strlower_n(label, r);
+ siphash24_compress(label, r, state);
+ siphash24_compress_byte(0, state); /* make sure foobar and foo.bar result in different hashes */
}
- return ul;
+ /* enforce that all names are terminated by the empty label */
+ string_hash_func("", state);
}
int dns_name_compare_func(const void *a, const void *b) {
const char *x, *y;
- int r, q, k, w;
+ int r, q;
assert(a);
assert(b);
@@ -420,7 +507,7 @@ int dns_name_compare_func(const void *a, const void *b) {
y = (const char *) b + strlen(b);
for (;;) {
- char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
+ char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
if (x == NULL && y == NULL)
return 0;
@@ -430,17 +517,7 @@ int dns_name_compare_func(const void *a, const void *b) {
if (r < 0 || q < 0)
return r - q;
- k = dns_label_undo_idna(la, r, la, sizeof(la));
- w = dns_label_undo_idna(lb, q, lb, sizeof(lb));
- if (k < 0 || w < 0)
- return k - w;
- if (k > 0)
- r = k;
- if (w > 0)
- r = w;
-
- la[r] = lb[q] = 0;
- r = strcasecmp(la, lb);
+ r = ascii_strcasecmp_nn(la, r, lb, q);
if (r != 0)
return r;
}
@@ -452,45 +529,35 @@ const struct hash_ops dns_name_hash_ops = {
};
int dns_name_equal(const char *x, const char *y) {
- int r, q, k, w;
+ int r, q;
assert(x);
assert(y);
for (;;) {
- char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
-
- if (*x == 0 && *y == 0)
- return true;
+ char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
r = dns_label_unescape(&x, la, sizeof(la));
if (r < 0)
return r;
- k = dns_label_undo_idna(la, r, la, sizeof(la));
- if (k < 0)
- return k;
- if (k > 0)
- r = k;
-
q = dns_label_unescape(&y, lb, sizeof(lb));
if (q < 0)
return q;
- w = dns_label_undo_idna(lb, q, lb, sizeof(lb));
- if (w < 0)
- return w;
- if (w > 0)
- q = w;
-
- la[r] = lb[q] = 0;
- if (strcasecmp(la, lb))
+
+ if (r != q)
+ return false;
+ if (r == 0)
+ return true;
+
+ if (ascii_strcasecmp_n(la, lb, r) != 0)
return false;
}
}
int dns_name_endswith(const char *name, const char *suffix) {
const char *n, *s, *saved_n = NULL;
- int r, q, k, w;
+ int r, q;
assert(name);
assert(suffix);
@@ -499,16 +566,11 @@ int dns_name_endswith(const char *name, const char *suffix) {
s = suffix;
for (;;) {
- char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1];
+ char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX];
r = dns_label_unescape(&n, ln, sizeof(ln));
if (r < 0)
return r;
- k = dns_label_undo_idna(ln, r, ln, sizeof(ln));
- if (k < 0)
- return k;
- if (k > 0)
- r = k;
if (!saved_n)
saved_n = n;
@@ -516,20 +578,13 @@ int dns_name_endswith(const char *name, const char *suffix) {
q = dns_label_unescape(&s, ls, sizeof(ls));
if (q < 0)
return q;
- w = dns_label_undo_idna(ls, q, ls, sizeof(ls));
- if (w < 0)
- return w;
- if (w > 0)
- q = w;
if (r == 0 && q == 0)
return true;
if (r == 0 && saved_n == n)
return false;
- ln[r] = ls[q] = 0;
-
- if (r != q || strcasecmp(ln, ls)) {
+ if (r != q || ascii_strcasecmp_n(ln, ls, r) != 0) {
/* Not the same, let's jump back, and try with the next label again */
s = suffix;
@@ -539,6 +594,91 @@ int dns_name_endswith(const char *name, const char *suffix) {
}
}
+int dns_name_startswith(const char *name, const char *prefix) {
+ const char *n, *p;
+ int r, q;
+
+ assert(name);
+ assert(prefix);
+
+ n = name;
+ p = prefix;
+
+ for (;;) {
+ char ln[DNS_LABEL_MAX], lp[DNS_LABEL_MAX];
+
+ r = dns_label_unescape(&p, lp, sizeof(lp));
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return true;
+
+ q = dns_label_unescape(&n, ln, sizeof(ln));
+ if (q < 0)
+ return q;
+
+ if (r != q)
+ return false;
+ if (ascii_strcasecmp_n(ln, lp, r) != 0)
+ return false;
+ }
+}
+
+int dns_name_change_suffix(const char *name, const char *old_suffix, const char *new_suffix, char **ret) {
+ const char *n, *s, *saved_before = NULL, *saved_after = NULL, *prefix;
+ int r, q;
+
+ assert(name);
+ assert(old_suffix);
+ assert(new_suffix);
+ assert(ret);
+
+ n = name;
+ s = old_suffix;
+
+ for (;;) {
+ char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX];
+
+ if (!saved_before)
+ saved_before = n;
+
+ r = dns_label_unescape(&n, ln, sizeof(ln));
+ if (r < 0)
+ return r;
+
+ if (!saved_after)
+ saved_after = n;
+
+ q = dns_label_unescape(&s, ls, sizeof(ls));
+ if (q < 0)
+ return q;
+
+ if (r == 0 && q == 0)
+ break;
+ if (r == 0 && saved_after == n) {
+ *ret = NULL; /* doesn't match */
+ return 0;
+ }
+
+ if (r != q || ascii_strcasecmp_n(ln, ls, r) != 0) {
+
+ /* Not the same, let's jump back, and try with the next label again */
+ s = old_suffix;
+ n = saved_after;
+ saved_after = saved_before = NULL;
+ }
+ }
+
+ /* Found it! Now generate the new name */
+ prefix = strndupa(name, saved_before - name);
+
+ r = dns_name_concat(prefix, new_suffix, ret);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
int dns_name_between(const char *a, const char *b, const char *c) {
int n;
@@ -677,34 +817,493 @@ int dns_name_address(const char *p, int *family, union in_addr_union *address) {
return 0;
}
-int dns_name_root(const char *name) {
- char label[DNS_LABEL_MAX+1];
- int r;
+bool dns_name_is_root(const char *name) {
assert(name);
- r = dns_label_unescape(&name, label, sizeof(label));
- if (r < 0)
- return r;
+ /* There are exactly two ways to encode the root domain name:
+ * as empty string, or with a single dot. */
- return r == 0 && *name == 0;
+ return STR_IN_SET(name, "", ".");
}
-int dns_name_single_label(const char *name) {
- char label[DNS_LABEL_MAX+1];
+bool dns_name_is_single_label(const char *name) {
int r;
assert(name);
- r = dns_label_unescape(&name, label, sizeof(label));
+ r = dns_name_parent(&name);
+ if (r <= 0)
+ return false;
+
+ return dns_name_is_root(name);
+}
+
+/* Encode a domain name according to RFC 1035 Section 3.1, without compression */
+int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, bool canonical) {
+ uint8_t *label_length, *out;
+ int r;
+
+ assert(domain);
+ assert(buffer);
+
+ out = buffer;
+
+ do {
+ /* Reserve a byte for label length */
+ if (len <= 0)
+ return -ENOBUFS;
+ len--;
+ label_length = out;
+ out++;
+
+ /* Convert and copy a single label. Note that
+ * dns_label_unescape() returns 0 when it hits the end
+ * of the domain name, which we rely on here to encode
+ * the trailing NUL byte. */
+ r = dns_label_unescape(&domain, (char *) out, len);
+ if (r < 0)
+ return r;
+
+ /* Optionally, output the name in DNSSEC canonical
+ * format, as described in RFC 4034, section 6.2. Or
+ * in other words: in lower-case. */
+ if (canonical)
+ ascii_strlower_n((char*) out, (size_t) r);
+
+ /* Fill label length, move forward */
+ *label_length = r;
+ out += r;
+ len -= r;
+
+ } while (r != 0);
+
+ /* Verify the maximum size of the encoded name. The trailing
+ * dot + NUL byte account are included this time, hence
+ * compare against DNS_HOSTNAME_MAX + 2 (which is 255) this
+ * time. */
+ if (out - buffer > DNS_HOSTNAME_MAX + 2)
+ return -EINVAL;
+
+ return out - buffer;
+}
+
+static bool srv_type_label_is_valid(const char *label, size_t n) {
+ size_t k;
+
+ assert(label);
+
+ if (n < 2) /* Label needs to be at least 2 chars long */
+ return false;
+
+ if (label[0] != '_') /* First label char needs to be underscore */
+ return false;
+
+ /* Second char must be a letter */
+ if (!(label[1] >= 'A' && label[1] <= 'Z') &&
+ !(label[1] >= 'a' && label[1] <= 'z'))
+ return false;
+
+ /* Third and further chars must be alphanumeric or a hyphen */
+ for (k = 2; k < n; k++) {
+ if (!(label[k] >= 'A' && label[k] <= 'Z') &&
+ !(label[k] >= 'a' && label[k] <= 'z') &&
+ !(label[k] >= '0' && label[k] <= '9') &&
+ label[k] != '-')
+ return false;
+ }
+
+ return true;
+}
+
+bool dns_srv_type_is_valid(const char *name) {
+ unsigned c = 0;
+ int r;
+
+ if (!name)
+ return false;
+
+ for (;;) {
+ char label[DNS_LABEL_MAX];
+
+ /* This more or less implements RFC 6335, Section 5.1 */
+
+ r = dns_label_unescape(&name, label, sizeof(label));
+ if (r < 0)
+ return false;
+ if (r == 0)
+ break;
+
+ if (c >= 2)
+ return false;
+
+ if (!srv_type_label_is_valid(label, r))
+ return false;
+
+ c++;
+ }
+
+ return c == 2; /* exactly two labels */
+}
+
+bool dns_service_name_is_valid(const char *name) {
+ size_t l;
+
+ /* This more or less implements RFC 6763, Section 4.1.1 */
+
+ if (!name)
+ return false;
+
+ if (!utf8_is_valid(name))
+ return false;
+
+ if (string_has_cc(name, NULL))
+ return false;
+
+ l = strlen(name);
+ if (l <= 0)
+ return false;
+ if (l > 63)
+ return false;
+
+ return true;
+}
+
+int dns_service_join(const char *name, const char *type, const char *domain, char **ret) {
+ char escaped[DNS_LABEL_ESCAPED_MAX];
+ _cleanup_free_ char *n = NULL;
+ int r;
+
+ assert(type);
+ assert(domain);
+ assert(ret);
+
+ if (!dns_srv_type_is_valid(type))
+ return -EINVAL;
+
+ if (!name)
+ return dns_name_concat(type, domain, ret);
+
+ if (!dns_service_name_is_valid(name))
+ return -EINVAL;
+
+ r = dns_label_escape(name, strlen(name), escaped, sizeof(escaped));
if (r < 0)
return r;
- if (r == 0)
- return 0;
- r = dns_label_unescape(&name, label, sizeof(label));
+ r = dns_name_concat(type, domain, &n);
+ if (r < 0)
+ return r;
+
+ return dns_name_concat(escaped, n, ret);
+}
+
+static bool dns_service_name_label_is_valid(const char *label, size_t n) {
+ char *s;
+
+ assert(label);
+
+ if (memchr(label, 0, n))
+ return false;
+
+ s = strndupa(label, n);
+ return dns_service_name_is_valid(s);
+}
+
+int dns_service_split(const char *joined, char **_name, char **_type, char **_domain) {
+ _cleanup_free_ char *name = NULL, *type = NULL, *domain = NULL;
+ const char *p = joined, *q = NULL, *d = NULL;
+ char a[DNS_LABEL_MAX], b[DNS_LABEL_MAX], c[DNS_LABEL_MAX];
+ int an, bn, cn, r;
+ unsigned x = 0;
+
+ assert(joined);
+
+ /* Get first label from the full name */
+ an = dns_label_unescape(&p, a, sizeof(a));
+ if (an < 0)
+ return an;
+
+ if (an > 0) {
+ x++;
+
+ /* If there was a first label, try to get the second one */
+ bn = dns_label_unescape(&p, b, sizeof(b));
+ if (bn < 0)
+ return bn;
+
+ if (bn > 0) {
+ x++;
+
+ /* If there was a second label, try to get the third one */
+ q = p;
+ cn = dns_label_unescape(&p, c, sizeof(c));
+ if (cn < 0)
+ return cn;
+
+ if (cn > 0)
+ x++;
+ } else
+ cn = 0;
+ } else
+ an = 0;
+
+ if (x >= 2 && srv_type_label_is_valid(b, bn)) {
+
+ if (x >= 3 && srv_type_label_is_valid(c, cn)) {
+
+ if (dns_service_name_label_is_valid(a, an)) {
+ /* OK, got <name> . <type> . <type2> . <domain> */
+
+ name = strndup(a, an);
+ if (!name)
+ return -ENOMEM;
+
+ type = strjoin(b, ".", c, NULL);
+ if (!type)
+ return -ENOMEM;
+
+ d = p;
+ goto finish;
+ }
+
+ } else if (srv_type_label_is_valid(a, an)) {
+
+ /* OK, got <type> . <type2> . <domain> */
+
+ name = NULL;
+
+ type = strjoin(a, ".", b, NULL);
+ if (!type)
+ return -ENOMEM;
+
+ d = q;
+ goto finish;
+ }
+ }
+
+ name = NULL;
+ type = NULL;
+ d = joined;
+
+finish:
+ r = dns_name_normalize(d, &domain);
if (r < 0)
return r;
- return r == 0 && *name == 0;
+ if (_domain) {
+ *_domain = domain;
+ domain = NULL;
+ }
+
+ if (_type) {
+ *_type = type;
+ type = NULL;
+ }
+
+ if (_name) {
+ *_name = name;
+ name = NULL;
+ }
+
+ return 0;
+}
+
+static int dns_name_build_suffix_table(const char *name, const char*table[]) {
+ const char *p;
+ unsigned n = 0;
+ int r;
+
+ assert(name);
+ assert(table);
+
+ p = name;
+ for (;;) {
+ if (n > DNS_N_LABELS_MAX)
+ return -EINVAL;
+
+ table[n] = p;
+ r = dns_name_parent(&p);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ n++;
+ }
+
+ return (int) n;
+}
+
+int dns_name_suffix(const char *name, unsigned n_labels, const char **ret) {
+ const char* labels[DNS_N_LABELS_MAX+1];
+ int n;
+
+ assert(name);
+ assert(ret);
+
+ n = dns_name_build_suffix_table(name, labels);
+ if (n < 0)
+ return n;
+
+ if ((unsigned) n < n_labels)
+ return -EINVAL;
+
+ *ret = labels[n - n_labels];
+ return (int) (n - n_labels);
+}
+
+int dns_name_skip(const char *a, unsigned n_labels, const char **ret) {
+ int r;
+
+ assert(a);
+ assert(ret);
+
+ for (; n_labels > 0; n_labels --) {
+ r = dns_name_parent(&a);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ *ret = "";
+ return 0;
+ }
+ }
+
+ *ret = a;
+ return 1;
+}
+
+int dns_name_count_labels(const char *name) {
+ unsigned n = 0;
+ const char *p;
+ int r;
+
+ assert(name);
+
+ p = name;
+ for (;;) {
+ r = dns_name_parent(&p);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ if (n >= DNS_N_LABELS_MAX)
+ return -EINVAL;
+
+ n++;
+ }
+
+ return (int) n;
+}
+
+int dns_name_equal_skip(const char *a, unsigned n_labels, const char *b) {
+ int r;
+
+ assert(a);
+ assert(b);
+
+ r = dns_name_skip(a, n_labels, &a);
+ if (r <= 0)
+ return r;
+
+ return dns_name_equal(a, b);
+}
+
+int dns_name_common_suffix(const char *a, const char *b, const char **ret) {
+ const char *a_labels[DNS_N_LABELS_MAX+1], *b_labels[DNS_N_LABELS_MAX+1];
+ int n = 0, m = 0, k = 0, r, q;
+
+ assert(a);
+ assert(b);
+ assert(ret);
+
+ /* Determines the common suffix of domain names a and b */
+
+ n = dns_name_build_suffix_table(a, a_labels);
+ if (n < 0)
+ return n;
+
+ m = dns_name_build_suffix_table(b, b_labels);
+ if (m < 0)
+ return m;
+
+ for (;;) {
+ char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
+ const char *x, *y;
+
+ if (k >= n || k >= m) {
+ *ret = a_labels[n - k];
+ return 0;
+ }
+
+ x = a_labels[n - 1 - k];
+ r = dns_label_unescape(&x, la, sizeof(la));
+ if (r < 0)
+ return r;
+
+ y = b_labels[m - 1 - k];
+ q = dns_label_unescape(&y, lb, sizeof(lb));
+ if (q < 0)
+ return q;
+
+ if (r != q || ascii_strcasecmp_n(la, lb, r) != 0) {
+ *ret = a_labels[n - k];
+ return 0;
+ }
+
+ k++;
+ }
+}
+
+int dns_name_apply_idna(const char *name, char **ret) {
+ _cleanup_free_ char *buf = NULL;
+ size_t n = 0, allocated = 0;
+ bool first = true;
+ int r, q;
+
+ assert(name);
+ assert(ret);
+
+ for (;;) {
+ char label[DNS_LABEL_MAX];
+
+ r = dns_label_unescape(&name, label, sizeof(label));
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ q = dns_label_apply_idna(label, r, label, sizeof(label));
+ if (q < 0)
+ return q;
+ if (q > 0)
+ r = q;
+
+ if (!GREEDY_REALLOC(buf, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
+ return -ENOMEM;
+
+ r = dns_label_escape(label, r, buf + n + !first, DNS_LABEL_ESCAPED_MAX);
+ if (r < 0)
+ return r;
+
+ if (first)
+ first = false;
+ else
+ buf[n++] = '.';
+
+ n +=r;
+ }
+
+ if (n > DNS_HOSTNAME_MAX)
+ return -EINVAL;
+
+ if (!GREEDY_REALLOC(buf, allocated, n + 1))
+ return -ENOMEM;
+
+ buf[n] = 0;
+ *ret = buf;
+ buf = NULL;
+
+ return (int) n;
}
diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h
index 8e73d9c20f..40c9ee5f27 100644
--- a/src/shared/dns-domain.h
+++ b/src/shared/dns-domain.h
@@ -22,15 +22,37 @@
#pragma once
+#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
#include "hashmap.h"
#include "in-addr-util.h"
+/* Length of a single label, with all escaping removed, excluding any trailing dot or NUL byte */
#define DNS_LABEL_MAX 63
-#define DNS_NAME_MAX 255
+
+/* Worst case length of a single label, with all escaping applied and room for a trailing NUL byte. */
+#define DNS_LABEL_ESCAPED_MAX (DNS_LABEL_MAX*4+1)
+
+/* Maximum length of a full hostname, consisting of a series of unescaped labels, and no trailing dot or NUL byte */
+#define DNS_HOSTNAME_MAX 253
+
+/* Maximum length of a full hostname, on the wire, including the final NUL byte */
+#define DNS_WIRE_FOMAT_HOSTNAME_MAX 255
+
+/* Maximum number of labels per valid hostname */
+#define DNS_N_LABELS_MAX 127
int dns_label_unescape(const char **name, char *dest, size_t sz);
int dns_label_unescape_suffix(const char *name, const char **label_end, char *dest, size_t sz);
-int dns_label_escape(const char *p, size_t l, char **ret);
+int dns_label_escape(const char *p, size_t l, char *dest, size_t sz);
+int dns_label_escape_new(const char *p, size_t l, char **ret);
+
+static inline int dns_name_parent(const char **name) {
+ return dns_label_unescape(name, NULL, DNS_LABEL_MAX);
+}
int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max);
int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max);
@@ -54,16 +76,37 @@ static inline int dns_name_is_valid(const char *s) {
return 1;
}
-unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_SIZE]);
+void dns_name_hash_func(const void *s, struct siphash *state);
int dns_name_compare_func(const void *a, const void *b);
extern const struct hash_ops dns_name_hash_ops;
int dns_name_between(const char *a, const char *b, const char *c);
int dns_name_equal(const char *x, const char *y);
int dns_name_endswith(const char *name, const char *suffix);
+int dns_name_startswith(const char *name, const char *prefix);
+
+int dns_name_change_suffix(const char *name, const char *old_suffix, const char *new_suffix, char **ret);
int dns_name_reverse(int family, const union in_addr_union *a, char **ret);
int dns_name_address(const char *p, int *family, union in_addr_union *a);
-int dns_name_root(const char *name);
-int dns_name_single_label(const char *name);
+bool dns_name_is_root(const char *name);
+bool dns_name_is_single_label(const char *name);
+
+int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, bool canonical);
+
+bool dns_srv_type_is_valid(const char *name);
+bool dns_service_name_is_valid(const char *name);
+
+int dns_service_join(const char *name, const char *type, const char *domain, char **ret);
+int dns_service_split(const char *joined, char **name, char **type, char **domain);
+
+int dns_name_suffix(const char *name, unsigned n_labels, const char **ret);
+int dns_name_count_labels(const char *name);
+
+int dns_name_skip(const char *a, unsigned n_labels, const char **ret);
+int dns_name_equal_skip(const char *a, unsigned n_labels, const char *b);
+
+int dns_name_common_suffix(const char *a, const char *b, const char **ret);
+
+int dns_name_apply_idna(const char *name, char **ret);
diff --git a/src/shared/dropin.c b/src/shared/dropin.c
index 963d05d32e..073a8396c5 100644
--- a/src/shared/dropin.c
+++ b/src/shared/dropin.c
@@ -19,12 +19,27 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <dirent.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "alloc-util.h"
+#include "conf-files.h"
#include "dropin.h"
-#include "util.h"
-#include "strv.h"
-#include "mkdir.h"
+#include "escape.h"
+#include "fd-util.h"
#include "fileio-label.h"
-#include "conf-files.h"
+#include "hashmap.h"
+#include "log.h"
+#include "macro.h"
+#include "mkdir.h"
+#include "path-util.h"
+#include "set.h"
+#include "string-util.h"
+#include "strv.h"
+#include "unit-name.h"
int drop_in_file(const char *dir, const char *unit, unsigned level,
const char *name, char **_p, char **_q) {
@@ -78,7 +93,7 @@ int write_drop_in(const char *dir, const char *unit, unsigned level,
if (r < 0)
return r;
- mkdir_p(p, 0755);
+ (void) mkdir_p(p, 0755);
return write_string_file_atomic_label(q, data);
}
@@ -132,8 +147,7 @@ static int iterate_dir(
if (errno == ENOENT)
return 0;
- log_error_errno(errno, "Failed to open directory %s: %m", path);
- return -errno;
+ return log_error_errno(errno, "Failed to open directory %s: %m", path);
}
for (;;) {
@@ -142,7 +156,7 @@ static int iterate_dir(
errno = 0;
de = readdir(d);
- if (!de && errno != 0)
+ if (!de && errno > 0)
return log_error_errno(errno, "Failed to read directory %s: %m", path);
if (!de)
diff --git a/src/shared/dropin.h b/src/shared/dropin.h
index d4531fca2d..a8d647e990 100644
--- a/src/shared/dropin.h
+++ b/src/shared/dropin.h
@@ -21,6 +21,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "hashmap.h"
#include "macro.h"
#include "set.h"
#include "unit-name.h"
diff --git a/src/shared/efivars.c b/src/shared/efivars.c
index f087c2a566..13af68d539 100644
--- a/src/shared/efivars.c
+++ b/src/shared/efivars.c
@@ -19,14 +19,30 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
-#include <string.h>
+#include <dirent.h>
+#include <errno.h>
#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
-#include "util.h"
+#include "sd-id128.h"
+
+#include "alloc-util.h"
+#include "dirent-util.h"
+#include "efivars.h"
+#include "fd-util.h"
+#include "io-util.h"
+#include "macro.h"
+#include "parse-util.h"
+#include "stdio-util.h"
+#include "time-util.h"
#include "utf8.h"
+#include "util.h"
#include "virt.h"
-#include "efivars.h"
#ifdef ENABLE_EFI
diff --git a/src/shared/efivars.h b/src/shared/efivars.h
index e953a12737..94af9717b0 100644
--- a/src/shared/efivars.h
+++ b/src/shared/efivars.h
@@ -22,8 +22,11 @@
***/
#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
#include "sd-id128.h"
+
#include "time-util.h"
#define EFI_VENDOR_LOADER SD_ID128_MAKE(4a,67,b0,82,0a,4c,41,cf,b6,c7,44,0b,29,bb,8c,4f)
diff --git a/src/shared/firewall-util.c b/src/shared/firewall-util.c
index effc6e8e70..9606122345 100644
--- a/src/shared/firewall-util.c
+++ b/src/shared/firewall-util.c
@@ -19,16 +19,23 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
+#include <alloca.h>
#include <arpa/inet.h>
+#include <endian.h>
+#include <errno.h>
#include <net/if.h>
+#include <stddef.h>
+#include <string.h>
+#include <sys/socket.h>
#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netfilter/nf_nat.h>
#include <linux/netfilter/xt_addrtype.h>
#include <libiptc/libiptc.h>
-#include "util.h"
+#include "alloc-util.h"
#include "firewall-util.h"
+#include "in-addr-util.h"
+#include "macro.h"
DEFINE_TRIVIAL_CLEANUP_FUNC(struct xtc_handle*, iptc_free);
diff --git a/src/shared/firewall-util.h b/src/shared/firewall-util.h
index 93152e3978..463e09bcaf 100644
--- a/src/shared/firewall-util.h
+++ b/src/shared/firewall-util.h
@@ -21,6 +21,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <stdbool.h>
+#include <stdint.h>
+
#include "in-addr-util.h"
#ifdef HAVE_LIBIPTC
diff --git a/src/shared/fstab-util.c b/src/shared/fstab-util.c
index e231a0ff80..d013901973 100644
--- a/src/shared/fstab-util.c
+++ b/src/shared/fstab-util.c
@@ -19,10 +19,38 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
+#include <mntent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "alloc-util.h"
+#include "device-nodes.h"
#include "fstab-util.h"
+#include "macro.h"
+#include "mount-util.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "string-util.h"
#include "strv.h"
#include "util.h"
+bool fstab_is_mount_point(const char *mount) {
+ _cleanup_endmntent_ FILE *f = NULL;
+ struct mntent *m;
+
+ f = setmntent("/etc/fstab", "r");
+ if (!f)
+ return false;
+
+ while ((m = getmntent(f)))
+ if (path_equal(m->mnt_dir, mount))
+ return true;
+
+ return false;
+}
+
int fstab_filter_options(const char *opts, const char *names,
const char **namefound, char **value, char **filtered) {
const char *name, *n = NULL, *x;
@@ -178,3 +206,60 @@ int fstab_find_pri(const char *options, int *ret) {
*ret = (int) pri;
return 1;
}
+
+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.
+ *
+ * DON'T USE THIS FOR NEW CODE ANYMORE!*/
+
+ l = strlen(s);
+ if (l < 2)
+ return strdup(s);
+
+ if (strchr(quotes, s[0]) && s[l-1] == s[0])
+ return strndup(s+1, l-2);
+
+ return strdup(s);
+}
+
+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, QUOTES);
+ if (!u)
+ return NULL;
+
+ enc_len = strlen(u) * 4 + 1;
+ t = new(char, enc_len);
+ if (!t)
+ return NULL;
+
+ if (encode_devnode_name(u, t, enc_len) < 0)
+ return NULL;
+
+ return strjoin("/dev/disk/by-", by, "/", t, NULL);
+}
+
+char *fstab_node_to_udev_node(const char *p) {
+ assert(p);
+
+ if (startswith(p, "LABEL="))
+ return tag_to_udev_node(p+6, "label");
+
+ if (startswith(p, "UUID="))
+ return tag_to_udev_node(p+5, "uuid");
+
+ if (startswith(p, "PARTUUID="))
+ return tag_to_udev_node(p+9, "partuuid");
+
+ if (startswith(p, "PARTLABEL="))
+ return tag_to_udev_node(p+10, "partlabel");
+
+ return strdup(p);
+}
diff --git a/src/shared/fstab-util.h b/src/shared/fstab-util.h
index 387c562a96..5ebea44019 100644
--- a/src/shared/fstab-util.h
+++ b/src/shared/fstab-util.h
@@ -23,10 +23,12 @@
#include <stdbool.h>
#include <stddef.h>
+
#include "macro.h"
-int fstab_filter_options(const char *opts, const char *names,
- const char **namefound, char **value, char **filtered);
+bool fstab_is_mount_point(const char *mount);
+
+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);
@@ -48,3 +50,5 @@ static inline bool fstab_test_yes_no_option(const char *opts, const char *yes_no
return opt == yes_no;
}
+
+char *fstab_node_to_udev_node(const char *p);
diff --git a/src/shared/generator.c b/src/shared/generator.c
index e58bbea77c..76808cbdd5 100644
--- a/src/shared/generator.c
+++ b/src/shared/generator.c
@@ -19,23 +19,30 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
#include <unistd.h>
-#include "util.h"
-#include "special.h"
-#include "mkdir.h"
-#include "unit-name.h"
+#include "alloc-util.h"
+#include "dropin.h"
+#include "escape.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fstab-util.h"
#include "generator.h"
+#include "log.h"
+#include "macro.h"
+#include "mkdir.h"
#include "path-util.h"
-#include "fstab-util.h"
-#include "fileio.h"
-#include "dropin.h"
+#include "special.h"
+#include "string-util.h"
+#include "time-util.h"
+#include "unit-name.h"
+#include "util.h"
static int write_fsck_sysroot_service(const char *dir, const char *what) {
- const char *unit;
- _cleanup_free_ char *device = NULL;
- _cleanup_free_ char *escaped;
+ _cleanup_free_ char *device = NULL, *escaped = NULL;
_cleanup_fclose_ FILE *f = NULL;
+ const char *unit;
int r;
escaped = cescape(what);
@@ -60,7 +67,7 @@ static int write_fsck_sysroot_service(const char *dir, const char *what) {
"Description=File System Check on %2$s\n"
"DefaultDependencies=no\n"
"BindsTo=%3$s\n"
- "After=%3$s\n"
+ "After=%3$s local-fs-pre.target\n"
"Before=shutdown.target\n"
"\n"
"[Service]\n"
@@ -101,16 +108,17 @@ int generator_write_fsck_deps(
if (!isempty(fstype) && !streq(fstype, "auto")) {
r = fsck_exists(fstype);
- if (r == -ENOENT) {
+ if (r < 0)
+ log_warning_errno(r, "Checking was requested for %s, but couldn't detect if fsck.%s may be used, proceeding: %m", what, fstype);
+ else if (r == 0) {
/* treat missing check as essentially OK */
- log_debug_errno(r, "Checking was requested for %s, but fsck.%s does not exist: %m", what, fstype);
+ log_debug("Checking was requested for %s, but fsck.%s does not exist.", what, fstype);
return 0;
- } else if (r < 0)
- return log_warning_errno(r, "Checking was requested for %s, but fsck.%s cannot be used: %m", what, fstype);
+ }
}
if (path_equal(where, "/")) {
- char *lnk;
+ const char *lnk;
lnk = strjoina(dir, "/" SPECIAL_LOCAL_FS_TARGET ".wants/systemd-fsck-root.service");
@@ -137,7 +145,7 @@ int generator_write_fsck_deps(
}
fprintf(f,
- "RequiresOverridable=%1$s\n"
+ "Requires=%1$s\n"
"After=%1$s\n",
fsck);
}
@@ -182,7 +190,6 @@ int generator_write_timeouts(
return write_drop_in_format(dir, unit, 50, "device-timeout",
"# Automatically generated by %s\n\n"
- "[Unit]\nJobTimeoutSec=" USEC_FMT,
- program_invocation_short_name,
- u / USEC_PER_SEC);
+ "[Unit]\nJobTimeoutSec=%s",
+ program_invocation_short_name, timeout);
}
diff --git a/src/shared/gpt.h b/src/shared/gpt.h
index add1df420f..5f4c00ba83 100644
--- a/src/shared/gpt.h
+++ b/src/shared/gpt.h
@@ -25,7 +25,7 @@
#include "sd-id128.h"
-/* We only support root disk discovery for x86, x86-64 and ARM for
+/* We only support root disk discovery for x86, x86-64, Itanium and ARM for
* now, since EFI for anything else doesn't really exist, and we only
* care for root partitions on the same disk as the EFI ESP. */
@@ -33,6 +33,7 @@
#define GPT_ROOT_X86_64 SD_ID128_MAKE(4f,68,bc,e3,e8,cd,4d,b1,96,e7,fb,ca,f9,84,b7,09)
#define GPT_ROOT_ARM SD_ID128_MAKE(69,da,d7,10,2c,e4,4e,3c,b1,6c,21,a1,d4,9a,be,d3)
#define GPT_ROOT_ARM_64 SD_ID128_MAKE(b9,21,b0,45,1d,f0,41,c3,af,44,4c,6f,28,0d,3f,ae)
+#define GPT_ROOT_IA64 SD_ID128_MAKE(99,3d,8d,3d,f8,0e,42,25,85,5a,9d,af,8e,d7,ea,97)
#define GPT_ESP SD_ID128_MAKE(c1,2a,73,28,f8,1f,11,d2,ba,4b,00,a0,c9,3e,c9,3b)
#define GPT_SWAP SD_ID128_MAKE(06,57,fd,6d,a4,ab,43,c4,84,e5,09,33,c8,4b,4f,4f)
@@ -46,6 +47,10 @@
# define GPT_ROOT_NATIVE GPT_ROOT_X86
#endif
+#if defined(__ia64__)
+# define GPT_ROOT_NATIVE GPT_ROOT_IA64
+#endif
+
#if defined(__aarch64__) && (__BYTE_ORDER != __BIG_ENDIAN)
# define GPT_ROOT_NATIVE GPT_ROOT_ARM_64
# define GPT_ROOT_SECONDARY GPT_ROOT_ARM
diff --git a/src/shared/import-util.c b/src/shared/import-util.c
index 001a8a37e8..ad400e8693 100644
--- a/src/shared/import-util.c
+++ b/src/shared/import-util.c
@@ -19,8 +19,18 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
+#include <errno.h>
+#include <string.h>
+
+#include "alloc-util.h"
+#include "btrfs-util.h"
#include "import-util.h"
+#include "log.h"
+#include "macro.h"
+#include "path-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "util.h"
int import_url_last_component(const char *url, char **ret) {
const char *e, *p;
@@ -150,54 +160,28 @@ 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;
+int import_assign_pool_quota_and_warn(const char *path) {
+ int r;
- if (isempty(name))
- return false;
-
- slash = strchr(name, '/');
- if (!slash)
- return false;
-
- if (!filename_is_valid(slash + 1))
- return false;
-
- p = strndupa(name, slash - name);
- if (!filename_is_valid(p))
- return false;
-
- return true;
-}
-
-bool dkr_id_is_valid(const char *id) {
-
- if (!filename_is_valid(id))
- return false;
-
- if (!in_charset(id, "0123456789abcdef"))
- return false;
+ r = btrfs_subvol_auto_qgroup("/var/lib/machines", 0, true);
+ if (r == -ENOTTY) {
+ log_debug_errno(r, "Failed to set up default quota hierarchy for /var/lib/machines, as directory is not on btrfs or not a subvolume. Ignoring.");
+ return 0;
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to set up default quota hierarchy for /var/lib/machines: %m");
+ if (r > 0)
+ log_info("Set up default quota hierarchy for /var/lib/machines.");
+
+ r = btrfs_subvol_auto_qgroup(path, 0, true);
+ if (r == -ENOTTY) {
+ log_debug_errno(r, "Failed to set up quota hierarchy for %s, as directory is not on btrfs or not a subvolume. Ignoring.", path);
+ return 0;
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to set up default quota hierarchy for %s: %m", path);
+ if (r > 0)
+ log_info("Set up default quota hierarchy for %s.", path);
- return true;
+ return 0;
}
diff --git a/src/shared/import-util.h b/src/shared/import-util.h
index 7bf7d4ca40..4bfa2d9aae 100644
--- a/src/shared/import-util.h
+++ b/src/shared/import-util.h
@@ -42,8 +42,4 @@ ImportVerify import_verify_from_string(const char *s) _pure_;
int tar_strip_suffixes(const char *name, char **ret);
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)
+int import_assign_pool_quota_and_warn(const char *path);
diff --git a/src/shared/install-printf.c b/src/shared/install-printf.c
index cbe984d2fb..645b3ce33c 100644
--- a/src/shared/install-printf.c
+++ b/src/shared/install-printf.c
@@ -19,13 +19,18 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include "formats-util.h"
+#include "install-printf.h"
+#include "install.h"
+#include "macro.h"
#include "specifier.h"
#include "unit-name.h"
-#include "util.h"
-#include "install-printf.h"
-#include "formats-util.h"
+#include "user-util.h"
static int specifier_prefix_and_instance(char specifier, void *data, void *userdata, char **ret) {
UnitFileInstallInfo *i = userdata;
@@ -65,42 +70,28 @@ static int specifier_instance(char specifier, void *data, void *userdata, char *
}
static int specifier_user_name(char specifier, void *data, void *userdata, char **ret) {
- UnitFileInstallInfo *i = userdata;
- const char *username;
- _cleanup_free_ char *tmp = NULL;
- char *printed = NULL;
-
- assert(i);
+ char *t;
- if (i->user)
- username = i->user;
- else
- /* get USER env from env or our own uid */
- username = tmp = getusername_malloc();
-
- switch (specifier) {
- case 'u':
- printed = strdup(username);
- break;
- case 'U': {
- /* fish username from passwd */
- uid_t uid;
- int r;
-
- r = get_user_creds(&username, &uid, NULL, NULL, NULL);
- if (r < 0)
- return r;
-
- if (asprintf(&printed, UID_FMT, uid) < 0)
- return -ENOMEM;
- break;
- }}
+ /* If we are UID 0 (root), this will not result in NSS,
+ * otherwise it might. This is good, as we want to be able to
+ * run this in PID 1, where our user ID is 0, but where NSS
+ * lookups are not allowed. */
+ t = getusername_malloc();
+ if (!t)
+ return -ENOMEM;
- *ret = printed;
+ *ret = t;
return 0;
}
+static int specifier_user_id(char specifier, void *data, void *userdata, char **ret) {
+
+ if (asprintf(ret, UID_FMT, getuid()) < 0)
+ return -ENOMEM;
+
+ return 0;
+}
int install_full_printf(UnitFileInstallInfo *i, const char *format, char **ret) {
@@ -112,8 +103,8 @@ int install_full_printf(UnitFileInstallInfo *i, const char *format, char **ret)
* %p: the prefix (foo)
* %i: the instance (bar)
- * %U the UID of the configured user or running user
- * %u the username of the configured user or running user
+ * %U the UID of the running user
+ * %u the username of running user
* %m the machine ID of the running system
* %H the host name of the running system
* %b the boot ID of the running system
@@ -126,7 +117,7 @@ int install_full_printf(UnitFileInstallInfo *i, const char *format, char **ret)
{ 'p', specifier_prefix, NULL },
{ 'i', specifier_instance, NULL },
- { 'U', specifier_user_name, NULL },
+ { 'U', specifier_user_id, NULL },
{ 'u', specifier_user_name, NULL },
{ 'm', specifier_machine_id, NULL },
diff --git a/src/shared/install.c b/src/shared/install.c
index 3d2b5ae77f..b37f8922df 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -19,42 +19,69 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
-#include <unistd.h>
-#include <string.h>
#include <fnmatch.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
-#include "util.h"
-#include "mkdir.h"
+#include "alloc-util.h"
+#include "conf-files.h"
+#include "conf-parser.h"
+#include "dirent-util.h"
+#include "extract-word.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
#include "hashmap.h"
-#include "set.h"
-#include "path-util.h"
+#include "install-printf.h"
+#include "install.h"
+#include "log.h"
+#include "macro.h"
+#include "mkdir.h"
#include "path-lookup.h"
+#include "path-util.h"
+#include "set.h"
+#include "special.h"
+#include "stat-util.h"
+#include "string-table.h"
+#include "string-util.h"
#include "strv.h"
#include "unit-name.h"
-#include "install.h"
-#include "conf-parser.h"
-#include "conf-files.h"
-#include "install-printf.h"
-#include "special.h"
+
+#define UNIT_FILE_FOLLOW_SYMLINK_MAX 64
+
+typedef enum SearchFlags {
+ SEARCH_LOAD = 1,
+ SEARCH_FOLLOW_CONFIG_SYMLINKS = 2,
+} SearchFlags;
typedef struct {
- OrderedHashmap *will_install;
- OrderedHashmap *have_installed;
+ OrderedHashmap *will_process;
+ OrderedHashmap *have_processed;
} InstallContext;
static int in_search_path(const char *path, char **search) {
_cleanup_free_ char *parent = NULL;
- int r;
+ char **i;
assert(path);
- r = path_get_parent(path, &parent);
- if (r < 0)
- return r;
+ parent = dirname_malloc(path);
+ if (!parent)
+ return -ENOMEM;
- return strv_contains(search, parent);
+ STRV_FOREACH(i, search)
+ if (path_equal(parent, *i))
+ return true;
+
+ return false;
}
static int get_config_path(UnitFileScope scope, bool runtime, const char *root_dir, char **ret) {
@@ -65,6 +92,9 @@ static int get_config_path(UnitFileScope scope, bool runtime, const char *root_d
assert(scope < _UNIT_FILE_SCOPE_MAX);
assert(ret);
+ /* This determines where we shall create or remove our
+ * installation ("configuration") symlinks */
+
switch (scope) {
case UNIT_FILE_SYSTEM:
@@ -95,9 +125,10 @@ static int get_config_path(UnitFileScope scope, bool runtime, const char *root_d
r = user_runtime_dir(&p);
else
r = user_config_home(&p);
-
- if (r <= 0)
- return r < 0 ? r : -ENOENT;
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENOENT;
break;
@@ -112,6 +143,185 @@ static int get_config_path(UnitFileScope scope, bool runtime, const char *root_d
return 0;
}
+static bool is_config_path(UnitFileScope scope, const char *path) {
+ int r;
+
+ assert(scope >= 0);
+ assert(scope < _UNIT_FILE_SCOPE_MAX);
+ assert(path);
+
+ /* Checks whether the specified path is intended for
+ * configuration or is outside of it */
+
+ switch (scope) {
+
+ case UNIT_FILE_SYSTEM:
+ case UNIT_FILE_GLOBAL:
+ return path_startswith(path, "/etc") ||
+ path_startswith(path, SYSTEM_CONFIG_UNIT_PATH) ||
+ path_startswith(path, "/run");
+
+
+ case UNIT_FILE_USER: {
+ _cleanup_free_ char *p = NULL;
+
+ r = user_config_home(&p);
+ if (r < 0)
+ return r;
+ if (r > 0 && path_startswith(path, p))
+ return true;
+
+ p = mfree(p);
+
+ r = user_runtime_dir(&p);
+ if (r < 0)
+ return r;
+ if (r > 0 && path_startswith(path, p))
+ return true;
+
+ return false;
+ }
+
+ default:
+ assert_not_reached("Bad scope");
+ }
+}
+
+
+static int verify_root_dir(UnitFileScope scope, const char **root_dir) {
+ int r;
+
+ assert(root_dir);
+
+ /* Verifies that the specified root directory to operate on
+ * makes sense. Reset it to NULL if it is the root directory
+ * or set to empty */
+
+ if (isempty(*root_dir) || path_equal(*root_dir, "/")) {
+ *root_dir = NULL;
+ return 0;
+ }
+
+ if (scope != UNIT_FILE_SYSTEM)
+ return -EINVAL;
+
+ r = is_dir(*root_dir, true);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENOTDIR;
+
+ return 0;
+}
+
+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;
+
+ assert(changes || n_changes == 0);
+
+ if (!changes)
+ return;
+
+ for (i = 0; i < n_changes; i++) {
+ free(changes[i].path);
+ free(changes[i].source);
+ }
+
+ free(changes);
+}
+
+static int create_symlink(
+ const char *old_path,
+ const char *new_path,
+ bool force,
+ UnitFileChange **changes,
+ unsigned *n_changes) {
+
+ _cleanup_free_ char *dest = NULL;
+ int r;
+
+ assert(old_path);
+ assert(new_path);
+
+ /* Actually create a symlink, and remember that we did. Is
+ * smart enough to check if there's already a valid symlink in
+ * place. */
+
+ mkdir_parents_label(new_path, 0755);
+
+ if (symlink(old_path, new_path) >= 0) {
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
+ return 0;
+ }
+
+ if (errno != EEXIST)
+ return -errno;
+
+ r = readlink_malloc(new_path, &dest);
+ if (r < 0)
+ return r;
+
+ if (path_equal(dest, old_path))
+ return 0;
+
+ if (!force)
+ return -EEXIST;
+
+ r = symlink_atomic(old_path, new_path);
+ if (r < 0)
+ return r;
+
+ 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 mark_symlink_for_removal(
Set **remove_symlinks_to,
const char *p) {
@@ -132,10 +342,12 @@ static int mark_symlink_for_removal(
path_kill_slashes(n);
r = set_consume(*remove_symlinks_to, n);
+ if (r == -EEXIST)
+ return 0;
if (r < 0)
- return r == -EEXIST ? 0 : r;
+ return r;
- return 0;
+ return 1;
}
static int remove_marked_symlinks_fd(
@@ -143,19 +355,19 @@ static int remove_marked_symlinks_fd(
int fd,
const char *path,
const char *config_path,
- bool *deleted,
+ bool *restart,
UnitFileChange **changes,
- unsigned *n_changes,
- char** instance_whitelist) {
+ unsigned *n_changes) {
_cleanup_closedir_ DIR *d = NULL;
+ struct dirent *de;
int r = 0;
assert(remove_symlinks_to);
assert(fd >= 0);
assert(path);
assert(config_path);
- assert(deleted);
+ assert(restart);
d = fdopendir(fd);
if (!d) {
@@ -165,27 +377,13 @@ static int remove_marked_symlinks_fd(
rewinddir(d);
- for (;;) {
- struct dirent *de;
-
- errno = 0;
- de = readdir(d);
- if (!de && errno != 0) {
- r = -errno;
- break;
- }
-
- if (!de)
- break;
-
- if (hidden_file(de->d_name))
- continue;
+ FOREACH_DIRENT(de, d, return -errno) {
dirent_ensure_type(d, de);
if (de->d_type == DT_DIR) {
- int nfd, q;
_cleanup_free_ char *p = NULL;
+ int nfd, q;
nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
if (nfd < 0) {
@@ -204,42 +402,23 @@ static int remove_marked_symlinks_fd(
}
/* This will close nfd, regardless whether it succeeds or not */
- q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, deleted, changes, n_changes, instance_whitelist);
+ q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, restart, changes, n_changes);
if (q < 0 && r == 0)
r = q;
} else if (de->d_type == DT_LNK) {
_cleanup_free_ char *p = NULL, *dest = NULL;
- int q;
bool found;
+ int q;
if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
continue;
- if (unit_name_is_valid(de->d_name, UNIT_NAME_INSTANCE) &&
- instance_whitelist &&
- !strv_contains(instance_whitelist, de->d_name)) {
-
- _cleanup_free_ char *w = NULL;
-
- /* OK, the file is not listed directly
- * in the whitelist, so let's check if
- * the template of it might be
- * listed. */
-
- r = unit_name_template(de->d_name, &w);
- if (r < 0)
- return r;
-
- if (!strv_contains(instance_whitelist, w))
- continue;
- }
-
p = path_make_absolute(de->d_name, path);
if (!p)
return -ENOMEM;
- q = readlink_and_canonicalize(p, &dest);
+ q = readlink_malloc(p, &dest);
if (q < 0) {
if (q == -ENOENT)
continue;
@@ -249,9 +428,15 @@ static int remove_marked_symlinks_fd(
continue;
}
+ /* We remove all links pointing to a file or
+ * path that is marked, as well as all files
+ * sharing the same name as a file that is
+ * marked. */
+
found =
- set_get(remove_symlinks_to, dest) ||
- set_get(remove_symlinks_to, basename(dest));
+ set_contains(remove_symlinks_to, dest) ||
+ set_contains(remove_symlinks_to, basename(dest)) ||
+ set_contains(remove_symlinks_to, de->d_name);
if (!found)
continue;
@@ -263,18 +448,15 @@ static int remove_marked_symlinks_fd(
}
path_kill_slashes(p);
- rmdir_parents(p, config_path);
- unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, p, NULL);
+ (void) rmdir_parents(p, config_path);
- if (!set_get(remove_symlinks_to, p)) {
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, p, NULL);
- q = mark_symlink_for_removal(&remove_symlinks_to, p);
- if (q < 0) {
- if (r == 0)
- r = q;
- } else
- *deleted = true;
- }
+ q = mark_symlink_for_removal(&remove_symlinks_to, p);
+ if (q < 0)
+ return q;
+ if (q > 0)
+ *restart = true;
}
}
@@ -285,12 +467,11 @@ static int remove_marked_symlinks(
Set *remove_symlinks_to,
const char *config_path,
UnitFileChange **changes,
- unsigned *n_changes,
- char** instance_whitelist) {
+ unsigned *n_changes) {
_cleanup_close_ int fd = -1;
+ bool restart;
int r = 0;
- bool deleted;
assert(config_path);
@@ -303,32 +484,32 @@ static int remove_marked_symlinks(
do {
int q, cfd;
- deleted = false;
+ restart = false;
cfd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
- if (cfd < 0) {
- r = -errno;
- break;
- }
+ if (cfd < 0)
+ return -errno;
/* This takes possession of cfd and closes it */
- q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, &deleted, changes, n_changes, instance_whitelist);
+ q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, &restart, changes, n_changes);
if (r == 0)
r = q;
- } while (deleted);
+ } while (restart);
return r;
}
static int find_symlinks_fd(
+ const char *root_dir,
const char *name,
int fd,
const char *path,
const char *config_path,
bool *same_name_link) {
- int r = 0;
_cleanup_closedir_ DIR *d = NULL;
+ struct dirent *de;
+ int r = 0;
assert(name);
assert(fd >= 0);
@@ -342,25 +523,13 @@ static int find_symlinks_fd(
return -errno;
}
- for (;;) {
- struct dirent *de;
-
- errno = 0;
- de = readdir(d);
- if (!de && errno != 0)
- return -errno;
-
- if (!de)
- return r;
-
- if (hidden_file(de->d_name))
- continue;
+ FOREACH_DIRENT(de, d, return -errno) {
dirent_ensure_type(d, de);
if (de->d_type == DT_DIR) {
- int nfd, q;
_cleanup_free_ char *p = NULL;
+ int nfd, q;
nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
if (nfd < 0) {
@@ -379,7 +548,7 @@ static int find_symlinks_fd(
}
/* This will close nfd, regardless whether it succeeds or not */
- q = find_symlinks_fd(name, nfd, p, config_path, same_name_link);
+ q = find_symlinks_fd(root_dir, name, nfd, p, config_path, same_name_link);
if (q > 0)
return 1;
if (r == 0)
@@ -396,16 +565,27 @@ static int find_symlinks_fd(
return -ENOMEM;
/* Acquire symlink destination */
- q = readlink_and_canonicalize(p, &dest);
+ q = readlink_malloc(p, &dest);
+ if (q == -ENOENT)
+ continue;
if (q < 0) {
- if (q == -ENOENT)
- continue;
-
if (r == 0)
r = q;
continue;
}
+ /* Make absolute */
+ if (!path_is_absolute(dest)) {
+ char *x;
+
+ x = prefix_root(root_dir, dest);
+ if (!x)
+ return -ENOMEM;
+
+ free(dest);
+ dest = x;
+ }
+
/* Check if the symlink itself matches what we
* are looking for */
if (path_is_absolute(name))
@@ -438,9 +618,12 @@ static int find_symlinks_fd(
return 1;
}
}
+
+ return r;
}
static int find_symlinks(
+ const char *root_dir,
const char *name,
const char *config_path,
bool *same_name_link) {
@@ -459,7 +642,7 @@ static int find_symlinks(
}
/* This takes possession of fd and closes it */
- return find_symlinks_fd(name, fd, config_path, config_path, same_name_link);
+ return find_symlinks_fd(root_dir, name, fd, config_path, config_path, same_name_link);
}
static int find_symlinks_in_scope(
@@ -468,350 +651,59 @@ static int find_symlinks_in_scope(
const char *name,
UnitFileState *state) {
- int r;
_cleanup_free_ char *normal_path = NULL, *runtime_path = NULL;
bool same_name_link_runtime = false, same_name_link = false;
+ int r;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
assert(name);
- /* First look in runtime config path */
- r = get_config_path(scope, true, root_dir, &normal_path);
+ /* First look in the normal config path */
+ r = get_config_path(scope, false, root_dir, &normal_path);
if (r < 0)
return r;
- r = find_symlinks(name, normal_path, &same_name_link_runtime);
+ r = find_symlinks(root_dir, name, normal_path, &same_name_link);
if (r < 0)
return r;
- else if (r > 0) {
- *state = UNIT_FILE_ENABLED_RUNTIME;
+ if (r > 0) {
+ *state = UNIT_FILE_ENABLED;
return r;
}
- /* Then look in the normal config path */
- r = get_config_path(scope, false, root_dir, &runtime_path);
+ /* Then look in runtime config path */
+ r = get_config_path(scope, true, root_dir, &runtime_path);
if (r < 0)
return r;
- r = find_symlinks(name, runtime_path, &same_name_link);
+ r = find_symlinks(root_dir, name, runtime_path, &same_name_link_runtime);
if (r < 0)
return r;
- else if (r > 0) {
- *state = UNIT_FILE_ENABLED;
+ if (r > 0) {
+ *state = UNIT_FILE_ENABLED_RUNTIME;
return r;
}
/* Hmm, we didn't find it, but maybe we found the same name
* link? */
- if (same_name_link_runtime) {
- *state = UNIT_FILE_LINKED_RUNTIME;
- return 1;
- } else if (same_name_link) {
+ if (same_name_link) {
*state = UNIT_FILE_LINKED;
return 1;
}
-
- return 0;
-}
-
-int unit_file_mask(
- UnitFileScope scope,
- bool runtime,
- const char *root_dir,
- char **files,
- bool force,
- UnitFileChange **changes,
- unsigned *n_changes) {
-
- char **i;
- _cleanup_free_ char *prefix = NULL;
- int r;
-
- assert(scope >= 0);
- assert(scope < _UNIT_FILE_SCOPE_MAX);
-
- r = get_config_path(scope, runtime, root_dir, &prefix);
- if (r < 0)
- return r;
-
- STRV_FOREACH(i, files) {
- _cleanup_free_ char *path = NULL;
-
- if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) {
- if (r == 0)
- r = -EINVAL;
- continue;
- }
-
- path = path_make_absolute(*i, prefix);
- if (!path) {
- r = -ENOMEM;
- break;
- }
-
- if (symlink("/dev/null", path) >= 0) {
- unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null");
- continue;
- }
-
- if (errno == EEXIST) {
-
- if (null_or_empty_path(path) > 0)
- continue;
-
- if (force) {
- if (symlink_atomic("/dev/null", path) >= 0) {
- 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;
- }
- }
-
- if (r == 0)
- r = -EEXIST;
- } else {
- if (r == 0)
- r = -errno;
- }
- }
-
- return r;
-}
-
-int unit_file_unmask(
- UnitFileScope scope,
- bool runtime,
- const char *root_dir,
- char **files,
- UnitFileChange **changes,
- unsigned *n_changes) {
-
- char **i, *config_path = NULL;
- int r, q;
- Set *remove_symlinks_to = NULL;
-
- assert(scope >= 0);
- assert(scope < _UNIT_FILE_SCOPE_MAX);
-
- r = get_config_path(scope, runtime, root_dir, &config_path);
- if (r < 0)
- goto finish;
-
- STRV_FOREACH(i, files) {
- _cleanup_free_ char *path = NULL;
-
- if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) {
- if (r == 0)
- r = -EINVAL;
- continue;
- }
-
- path = path_make_absolute(*i, config_path);
- if (!path) {
- r = -ENOMEM;
- break;
- }
-
- q = null_or_empty_path(path);
- if (q > 0) {
- if (unlink(path) < 0)
- q = -errno;
- else {
- q = mark_symlink_for_removal(&remove_symlinks_to, path);
- unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
- }
- }
-
- if (q != -ENOENT && r == 0)
- r = q;
- }
-
-
-finish:
- q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files);
- if (r == 0)
- r = q;
-
- set_free_free(remove_symlinks_to);
- free(config_path);
-
- return r;
-}
-
-int unit_file_link(
- UnitFileScope scope,
- bool runtime,
- const char *root_dir,
- char **files,
- bool force,
- UnitFileChange **changes,
- unsigned *n_changes) {
-
- _cleanup_lookup_paths_free_ LookupPaths paths = {};
- char **i;
- _cleanup_free_ char *config_path = NULL;
- int r, q;
-
- assert(scope >= 0);
- assert(scope < _UNIT_FILE_SCOPE_MAX);
-
- r = lookup_paths_init_from_scope(&paths, scope, root_dir);
- if (r < 0)
- return r;
-
- r = get_config_path(scope, runtime, root_dir, &config_path);
- if (r < 0)
- return r;
-
- STRV_FOREACH(i, files) {
- _cleanup_free_ char *path = NULL;
- char *fn;
- struct stat st;
-
- fn = basename(*i);
-
- if (!path_is_absolute(*i) ||
- !unit_name_is_valid(fn, UNIT_NAME_ANY)) {
- if (r == 0)
- r = -EINVAL;
- continue;
- }
-
- if (lstat(*i, &st) < 0) {
- if (r == 0)
- r = -errno;
- continue;
- }
-
- if (!S_ISREG(st.st_mode)) {
- r = -ENOENT;
- continue;
- }
-
- q = in_search_path(*i, paths.unit_path);
- if (q < 0)
- return q;
-
- if (q > 0)
- continue;
-
- path = path_make_absolute(fn, config_path);
- if (!path)
- return -ENOMEM;
-
- if (symlink(*i, path) >= 0) {
- unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, path, *i);
- continue;
- }
-
- if (errno == EEXIST) {
- _cleanup_free_ char *dest = NULL;
-
- q = readlink_and_make_absolute(path, &dest);
- if (q < 0 && errno != ENOENT) {
- if (r == 0)
- r = q;
- continue;
- }
-
- if (q >= 0 && path_equal(dest, *i))
- continue;
-
- if (force) {
- if (symlink_atomic(*i, path) >= 0) {
- 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;
- }
- }
-
- if (r == 0)
- r = -EEXIST;
- } else {
- if (r == 0)
- r = -errno;
- }
- }
-
- return r;
-}
-
-void unit_file_list_free(Hashmap *h) {
- UnitFileList *i;
-
- while ((i = hashmap_steal_first(h))) {
- free(i->path);
- free(i);
+ if (same_name_link_runtime) {
+ *state = UNIT_FILE_LINKED_RUNTIME;
+ return 1;
}
- 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;
-
- assert(changes || n_changes == 0);
+static void install_info_free(UnitFileInstallInfo *i) {
- if (!changes)
+ if (!i)
return;
- for (i = 0; i < n_changes; i++) {
- free(changes[i].path);
- free(changes[i].source);
- }
-
- free(changes);
-}
-
-static void install_info_free(UnitFileInstallInfo *i) {
- assert(i);
-
free(i->name);
free(i->path);
strv_free(i->aliases);
@@ -819,34 +711,45 @@ static void install_info_free(UnitFileInstallInfo *i) {
strv_free(i->required_by);
strv_free(i->also);
free(i->default_instance);
+ free(i->symlink_target);
free(i);
}
-static void install_info_hashmap_free(OrderedHashmap *m) {
+static OrderedHashmap* install_info_hashmap_free(OrderedHashmap *m) {
UnitFileInstallInfo *i;
if (!m)
- return;
+ return NULL;
while ((i = ordered_hashmap_steal_first(m)))
install_info_free(i);
- ordered_hashmap_free(m);
+ return ordered_hashmap_free(m);
}
static void install_context_done(InstallContext *c) {
assert(c);
- install_info_hashmap_free(c->will_install);
- install_info_hashmap_free(c->have_installed);
+ c->will_process = install_info_hashmap_free(c->will_process);
+ c->have_processed = install_info_hashmap_free(c->have_processed);
+}
- c->will_install = c->have_installed = NULL;
+static UnitFileInstallInfo *install_info_find(InstallContext *c, const char *name) {
+ UnitFileInstallInfo *i;
+
+ i = ordered_hashmap_get(c->have_processed, name);
+ if (i)
+ return i;
+
+ return ordered_hashmap_get(c->will_process, name);
}
static int install_info_add(
InstallContext *c,
const char *name,
- const char *path) {
+ const char *path,
+ UnitFileInstallInfo **ret) {
+
UnitFileInstallInfo *i = NULL;
int r;
@@ -859,17 +762,21 @@ static int install_info_add(
if (!unit_name_is_valid(name, UNIT_NAME_ANY))
return -EINVAL;
- if (ordered_hashmap_get(c->have_installed, name) ||
- ordered_hashmap_get(c->will_install, name))
+ i = install_info_find(c, name);
+ if (i) {
+ if (ret)
+ *ret = i;
return 0;
+ }
- r = ordered_hashmap_ensure_allocated(&c->will_install, &string_hash_ops);
+ r = ordered_hashmap_ensure_allocated(&c->will_process, &string_hash_ops);
if (r < 0)
return r;
i = new0(UnitFileInstallInfo, 1);
if (!i)
return -ENOMEM;
+ i->type = _UNIT_FILE_TYPE_INVALID;
i->name = strdup(name);
if (!i->name) {
@@ -885,30 +792,32 @@ static int install_info_add(
}
}
- r = ordered_hashmap_put(c->will_install, i->name, i);
+ r = ordered_hashmap_put(c->will_process, i->name, i);
if (r < 0)
goto fail;
+ if (ret)
+ *ret = i;
+
return 0;
fail:
- if (i)
- install_info_free(i);
-
+ install_info_free(i);
return r;
}
static int install_info_add_auto(
InstallContext *c,
- const char *name_or_path) {
+ const char *name_or_path,
+ UnitFileInstallInfo **ret) {
assert(c);
assert(name_or_path);
if (path_is_absolute(name_or_path))
- return install_info_add(c, NULL, name_or_path);
+ return install_info_add(c, NULL, name_or_path, ret);
else
- return install_info_add(c, name_or_path, NULL);
+ return install_info_add(c, name_or_path, NULL, ret);
}
static int config_parse_also(
@@ -923,64 +832,33 @@ static int config_parse_also(
void *data,
void *userdata) {
- size_t l;
- const char *word, *state;
- InstallContext *c = data;
UnitFileInstallInfo *i = userdata;
+ InstallContext *c = data;
+ int r;
assert(filename);
assert(lvalue);
assert(rvalue);
- FOREACH_WORD_QUOTED(word, l, rvalue, state) {
- _cleanup_free_ char *n;
- int r;
-
- n = strndup(word, l);
- if (!n)
- return -ENOMEM;
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
- r = install_info_add(c, n, NULL);
+ r = extract_first_word(&rvalue, &word, NULL, 0);
if (r < 0)
return r;
+ if (r == 0)
+ break;
- r = strv_extend(&i->also, n);
+ r = install_info_add(c, word, NULL, NULL);
if (r < 0)
return r;
- }
- if (!isempty(state))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Trailing garbage, ignoring.");
-
- return 0;
-}
-
-static int config_parse_user(
- 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) {
-
- UnitFileInstallInfo *i = data;
- char *printed;
- int r;
- assert(filename);
- assert(lvalue);
- assert(rvalue);
-
- r = install_full_printf(i, rvalue, &printed);
- if (r < 0)
- return r;
+ r = strv_push(&i->also, word);
+ if (r < 0)
+ return r;
- free(i->user);
- i->user = printed;
+ word = NULL;
+ }
return 0;
}
@@ -1025,9 +903,7 @@ static int unit_file_load(
UnitFileInstallInfo *info,
const char *path,
const char *root_dir,
- bool allow_symlink,
- bool load,
- bool *also) {
+ SearchFlags flags) {
const ConfigTableItem items[] = {
{ "Install", "Alias", config_parse_strv, 0, &info->aliases },
@@ -1035,34 +911,57 @@ static int unit_file_load(
{ "Install", "RequiredBy", config_parse_strv, 0, &info->required_by },
{ "Install", "DefaultInstance", config_parse_default_instance, 0, info },
{ "Install", "Also", config_parse_also, 0, c },
- { "Exec", "User", config_parse_user, 0, info },
{}
};
_cleanup_fclose_ FILE *f = NULL;
- int fd, r;
+ _cleanup_close_ int fd = -1;
+ struct stat st;
+ int r;
assert(c);
assert(info);
assert(path);
- if (!isempty(root_dir))
- path = strjoina(root_dir, "/", path);
+ path = prefix_roota(root_dir, path);
- if (!load) {
- r = access(path, F_OK) ? -errno : 0;
- return r;
+ if (!(flags & SEARCH_LOAD)) {
+ r = lstat(path, &st);
+ if (r < 0)
+ return -errno;
+
+ if (null_or_empty(&st))
+ info->type = UNIT_FILE_TYPE_MASKED;
+ else if (S_ISREG(st.st_mode))
+ info->type = UNIT_FILE_TYPE_REGULAR;
+ else if (S_ISLNK(st.st_mode))
+ return -ELOOP;
+ else if (S_ISDIR(st.st_mode))
+ return -EISDIR;
+ else
+ return -ENOTTY;
+
+ return 0;
}
- fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|(allow_symlink ? 0 : O_NOFOLLOW));
+ fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
if (fd < 0)
return -errno;
+ if (fstat(fd, &st) < 0)
+ return -errno;
+ if (null_or_empty(&st)) {
+ info->type = UNIT_FILE_TYPE_MASKED;
+ return 0;
+ }
+ if (S_ISDIR(st.st_mode))
+ return -EISDIR;
+ if (!S_ISREG(st.st_mode))
+ return -ENOTTY;
f = fdopen(fd, "re");
- if (!f) {
- safe_close(fd);
- return -ENOMEM;
- }
+ if (!f)
+ return -errno;
+ fd = -1;
r = config_parse(NULL, path, f,
NULL,
@@ -1071,8 +970,7 @@ static int unit_file_load(
if (r < 0)
return r;
- if (also)
- *also = !strv_isempty(info->also);
+ info->type = UNIT_FILE_TYPE_REGULAR;
return
(int) strv_length(info->aliases) +
@@ -1080,14 +978,73 @@ static int unit_file_load(
(int) strv_length(info->required_by);
}
+static int unit_file_load_or_readlink(
+ InstallContext *c,
+ UnitFileInstallInfo *info,
+ const char *path,
+ const char *root_dir,
+ SearchFlags flags) {
+
+ _cleanup_free_ char *np = NULL;
+ int r;
+
+ r = unit_file_load(c, info, path, root_dir, flags);
+ if (r != -ELOOP)
+ return r;
+
+ /* This is a symlink, let's read it. */
+
+ r = readlink_and_make_absolute_root(root_dir, path, &np);
+ if (r < 0)
+ return r;
+
+ if (path_equal(np, "/dev/null"))
+ info->type = UNIT_FILE_TYPE_MASKED;
+ else {
+ const char *bn;
+ UnitType a, b;
+
+ bn = basename(np);
+
+ if (unit_name_is_valid(info->name, UNIT_NAME_PLAIN)) {
+
+ if (!unit_name_is_valid(bn, UNIT_NAME_PLAIN))
+ return -EINVAL;
+
+ } else if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) {
+
+ if (!unit_name_is_valid(bn, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
+ return -EINVAL;
+
+ } else if (unit_name_is_valid(info->name, UNIT_NAME_TEMPLATE)) {
+
+ if (!unit_name_is_valid(bn, UNIT_NAME_TEMPLATE))
+ return -EINVAL;
+ } else
+ return -EINVAL;
+
+ /* Enforce that the symlink destination does not
+ * change the unit file type. */
+
+ a = unit_name_to_type(info->name);
+ b = unit_name_to_type(bn);
+ if (a < 0 || b < 0 || a != b)
+ return -EINVAL;
+
+ info->type = UNIT_FILE_TYPE_SYMLINK;
+ info->symlink_target = np;
+ np = NULL;
+ }
+
+ return 0;
+}
+
static int unit_file_search(
InstallContext *c,
UnitFileInstallInfo *info,
const LookupPaths *paths,
const char *root_dir,
- bool allow_symlink,
- bool load,
- bool *also) {
+ SearchFlags flags) {
char **p;
int r;
@@ -1096,8 +1053,12 @@ static int unit_file_search(
assert(info);
assert(paths);
+ /* Was this unit already loaded? */
+ if (info->type != _UNIT_FILE_TYPE_INVALID)
+ return 0;
+
if (info->path)
- return unit_file_load(c, info, info->path, root_dir, allow_symlink, load, also);
+ return unit_file_load_or_readlink(c, info, info->path, root_dir, flags);
assert(info->name);
@@ -1108,14 +1069,15 @@ static int unit_file_search(
if (!path)
return -ENOMEM;
- r = unit_file_load(c, info, path, root_dir, allow_symlink, load, also);
- if (r >= 0) {
+ r = unit_file_load_or_readlink(c, info, path, root_dir, flags);
+ if (r < 0) {
+ if (r != -ENOENT)
+ return r;
+ } else {
info->path = path;
path = NULL;
return r;
}
- if (r != -ENOENT && r != -ELOOP)
- return r;
}
if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) {
@@ -1137,92 +1099,149 @@ static int unit_file_search(
if (!path)
return -ENOMEM;
- r = unit_file_load(c, info, path, root_dir, allow_symlink, load, also);
- if (r >= 0) {
+ r = unit_file_load_or_readlink(c, info, path, root_dir, flags);
+ if (r < 0) {
+ if (r != -ENOENT)
+ return r;
+ } else {
info->path = path;
path = NULL;
return r;
}
- if (r != -ENOENT && r != -ELOOP)
- return r;
}
}
return -ENOENT;
}
-static int unit_file_can_install(
- const LookupPaths *paths,
+static int install_info_follow(
+ InstallContext *c,
+ UnitFileInstallInfo *i,
const char *root_dir,
- const char *name,
- bool allow_symlink,
- bool *also) {
+ SearchFlags flags) {
+
+ assert(c);
+ assert(i);
+
+ if (i->type != UNIT_FILE_TYPE_SYMLINK)
+ return -EINVAL;
+ if (!i->symlink_target)
+ return -EINVAL;
+
+ /* If the basename doesn't match, the caller should add a
+ * complete new entry for this. */
+
+ if (!streq(basename(i->symlink_target), i->name))
+ return -EXDEV;
+
+ free(i->path);
+ i->path = i->symlink_target;
+ i->symlink_target = NULL;
+ i->type = _UNIT_FILE_TYPE_INVALID;
+
+ return unit_file_load_or_readlink(c, i, i->path, root_dir, flags);
+}
+
+static int install_info_traverse(
+ UnitFileScope scope,
+ InstallContext *c,
+ const char *root_dir,
+ const LookupPaths *paths,
+ UnitFileInstallInfo *start,
+ SearchFlags flags,
+ UnitFileInstallInfo **ret) {
- _cleanup_(install_context_done) InstallContext c = {};
UnitFileInstallInfo *i;
+ unsigned k = 0;
int r;
assert(paths);
- assert(name);
+ assert(start);
+ assert(c);
- r = install_info_add_auto(&c, name);
+ r = unit_file_search(c, start, paths, root_dir, flags);
if (r < 0)
return r;
- assert_se(i = ordered_hashmap_first(c.will_install));
+ i = start;
+ while (i->type == UNIT_FILE_TYPE_SYMLINK) {
+ /* Follow the symlink */
- r = unit_file_search(&c, i, paths, root_dir, allow_symlink, true, also);
+ if (++k > UNIT_FILE_FOLLOW_SYMLINK_MAX)
+ return -ELOOP;
- if (r >= 0)
- r =
- (int) strv_length(i->aliases) +
- (int) strv_length(i->wanted_by) +
- (int) strv_length(i->required_by);
+ if (!(flags & SEARCH_FOLLOW_CONFIG_SYMLINKS) && is_config_path(scope, i->path))
+ return -ELOOP;
- return r;
-}
+ r = install_info_follow(c, i, root_dir, flags);
+ if (r < 0) {
+ _cleanup_free_ char *buffer = NULL;
+ const char *bn;
-static int create_symlink(
- const char *old_path,
- const char *new_path,
- bool force,
- UnitFileChange **changes,
- unsigned *n_changes) {
+ if (r != -EXDEV)
+ return r;
- _cleanup_free_ char *dest = NULL;
- int r;
+ /* Target has a different name, create a new
+ * install info object for that, and continue
+ * with that. */
- assert(old_path);
- assert(new_path);
+ bn = basename(i->symlink_target);
- mkdir_parents_label(new_path, 0755);
+ if (unit_name_is_valid(i->name, UNIT_NAME_INSTANCE) &&
+ unit_name_is_valid(bn, UNIT_NAME_TEMPLATE)) {
- if (symlink(old_path, new_path) >= 0) {
- unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
- return 0;
+ _cleanup_free_ char *instance = NULL;
+
+ r = unit_name_to_instance(i->name, &instance);
+ if (r < 0)
+ return r;
+
+ r = unit_name_replace_instance(bn, instance, &buffer);
+ if (r < 0)
+ return r;
+
+ bn = buffer;
+ }
+
+ r = install_info_add(c, bn, NULL, &i);
+ if (r < 0)
+ return r;
+
+ r = unit_file_search(c, i, paths, root_dir, flags);
+ if (r < 0)
+ return r;
+ }
+
+ /* Try again, with the new target we found. */
}
- if (errno != EEXIST)
- return -errno;
+ if (ret)
+ *ret = i;
- r = readlink_and_make_absolute(new_path, &dest);
- if (r < 0)
- return r;
+ return 0;
+}
- if (path_equal(dest, old_path))
- return 0;
+static int install_info_discover(
+ UnitFileScope scope,
+ InstallContext *c,
+ const char *root_dir,
+ const LookupPaths *paths,
+ const char *name,
+ SearchFlags flags,
+ UnitFileInstallInfo **ret) {
- if (!force)
- return -EEXIST;
+ UnitFileInstallInfo *i;
+ int r;
- r = symlink_atomic(old_path, new_path);
+ assert(c);
+ assert(paths);
+ assert(name);
+
+ r = install_info_add_auto(c, name, &i);
if (r < 0)
return r;
- 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;
+ return install_info_traverse(scope, c, root_dir, paths, i, flags, ret);
}
static int install_info_symlink_alias(
@@ -1357,6 +1376,9 @@ static int install_info_apply(
assert(paths);
assert(config_path);
+ if (i->type != UNIT_FILE_TYPE_REGULAR)
+ return 0;
+
r = install_info_symlink_alias(i, config_path, force, changes, n_changes);
q = install_info_symlink_wants(i, config_path, i->wanted_by, ".wants/", force, changes, n_changes);
@@ -1375,53 +1397,59 @@ static int install_info_apply(
}
static int install_context_apply(
+ UnitFileScope scope,
InstallContext *c,
const LookupPaths *paths,
const char *config_path,
const char *root_dir,
bool force,
+ SearchFlags flags,
UnitFileChange **changes,
unsigned *n_changes) {
UnitFileInstallInfo *i;
- int r, q;
+ int r;
assert(c);
assert(paths);
assert(config_path);
- if (!ordered_hashmap_isempty(c->will_install)) {
- r = ordered_hashmap_ensure_allocated(&c->have_installed, &string_hash_ops);
- if (r < 0)
- return r;
+ if (ordered_hashmap_isempty(c->will_process))
+ return 0;
- r = ordered_hashmap_reserve(c->have_installed, ordered_hashmap_size(c->will_install));
- if (r < 0)
- return r;
- }
+ r = ordered_hashmap_ensure_allocated(&c->have_processed, &string_hash_ops);
+ if (r < 0)
+ return r;
r = 0;
- while ((i = ordered_hashmap_first(c->will_install))) {
- assert_se(ordered_hashmap_move_one(c->have_installed, c->will_install, i->name) == 0);
+ while ((i = ordered_hashmap_first(c->will_process))) {
+ int q;
- q = unit_file_search(c, i, paths, root_dir, false, true, NULL);
- if (q < 0) {
- if (r >= 0)
- r = q;
+ q = ordered_hashmap_move_one(c->have_processed, c->will_process, i->name);
+ if (q < 0)
+ return q;
+ r = install_info_traverse(scope, c, root_dir, paths, i, flags, NULL);
+ if (r < 0)
return r;
- } else if (r >= 0)
- r += q;
+
+ if (i->type != UNIT_FILE_TYPE_REGULAR)
+ continue;
q = install_info_apply(i, paths, config_path, root_dir, force, changes, n_changes);
- if (r >= 0 && q < 0)
- r = q;
+ if (r >= 0) {
+ if (q < 0)
+ r = q;
+ else
+ r+= q;
+ }
}
return r;
}
static int install_context_mark_for_removal(
+ UnitFileScope scope,
InstallContext *c,
const LookupPaths *paths,
Set **remove_symlinks_to,
@@ -1429,7 +1457,7 @@ static int install_context_mark_for_removal(
const char *root_dir) {
UnitFileInstallInfo *i;
- int r, q;
+ int r;
assert(c);
assert(paths);
@@ -1437,87 +1465,182 @@ static int install_context_mark_for_removal(
/* Marks all items for removal */
- if (!ordered_hashmap_isempty(c->will_install)) {
- r = ordered_hashmap_ensure_allocated(&c->have_installed, &string_hash_ops);
+ if (ordered_hashmap_isempty(c->will_process))
+ return 0;
+
+ r = ordered_hashmap_ensure_allocated(&c->have_processed, &string_hash_ops);
+ if (r < 0)
+ return r;
+
+ while ((i = ordered_hashmap_first(c->will_process))) {
+
+ r = ordered_hashmap_move_one(c->have_processed, c->will_process, i->name);
if (r < 0)
return r;
- r = ordered_hashmap_reserve(c->have_installed, ordered_hashmap_size(c->will_install));
+ r = install_info_traverse(scope, c, root_dir, paths, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL);
if (r < 0)
return r;
- }
- r = 0;
- while ((i = ordered_hashmap_first(c->will_install))) {
- assert_se(ordered_hashmap_move_one(c->have_installed, c->will_install, i->name) == 0);
-
- q = unit_file_search(c, i, paths, root_dir, false, true, NULL);
- if (q == -ENOENT) {
- /* do nothing */
- } else if (q < 0) {
- if (r >= 0)
- r = q;
+ if (i->type != UNIT_FILE_TYPE_REGULAR)
+ continue;
+ r = mark_symlink_for_removal(remove_symlinks_to, i->name);
+ if (r < 0)
return r;
- } else if (r >= 0)
- r += q;
-
- 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_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);
- else
- /* does not exist, thus we will mark for removal symlinks
- * to template unit file */
- q = mark_symlink_for_removal(remove_symlinks_to, unit_file);
- } else {
- /* 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. */
- 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);
- }
- } else
- q = mark_symlink_for_removal(remove_symlinks_to, i->name);
+ return 0;
+}
- if (r >= 0 && q < 0)
+int unit_file_mask(
+ UnitFileScope scope,
+ bool runtime,
+ const char *root_dir,
+ char **files,
+ bool force,
+ UnitFileChange **changes,
+ unsigned *n_changes) {
+
+ _cleanup_free_ char *prefix = NULL;
+ char **i;
+ int r;
+
+ assert(scope >= 0);
+ assert(scope < _UNIT_FILE_SCOPE_MAX);
+
+ r = verify_root_dir(scope, &root_dir);
+ if (r < 0)
+ return r;
+
+ r = get_config_path(scope, runtime, root_dir, &prefix);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(i, files) {
+ _cleanup_free_ char *path = NULL;
+ int q;
+
+ if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) {
+ if (r == 0)
+ r = -EINVAL;
+ continue;
+ }
+
+ path = path_make_absolute(*i, prefix);
+ if (!path)
+ return -ENOMEM;
+
+ q = create_symlink("/dev/null", path, force, changes, n_changes);
+ if (q < 0 && r >= 0)
r = q;
}
return r;
}
-int unit_file_add_dependency(
+int unit_file_unmask(
+ UnitFileScope scope,
+ bool runtime,
+ const char *root_dir,
+ char **files,
+ UnitFileChange **changes,
+ unsigned *n_changes) {
+
+ _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
+ _cleanup_free_ char *config_path = NULL;
+ _cleanup_free_ char **todo = NULL;
+ size_t n_todo = 0, n_allocated = 0;
+ char **i;
+ int r, q;
+
+ assert(scope >= 0);
+ assert(scope < _UNIT_FILE_SCOPE_MAX);
+
+ r = verify_root_dir(scope, &root_dir);
+ if (r < 0)
+ return r;
+
+ r = get_config_path(scope, runtime, root_dir, &config_path);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(i, files) {
+ _cleanup_free_ char *path = NULL;
+
+ if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
+ return -EINVAL;
+
+ path = path_make_absolute(*i, config_path);
+ if (!path)
+ return -ENOMEM;
+
+ r = null_or_empty_path(path);
+ if (r == -ENOENT)
+ continue;
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
+ return -ENOMEM;
+
+ todo[n_todo++] = *i;
+ }
+
+ strv_uniq(todo);
+
+ r = 0;
+ STRV_FOREACH(i, todo) {
+ _cleanup_free_ char *path = NULL;
+
+ path = path_make_absolute(*i, config_path);
+ if (!path)
+ return -ENOMEM;
+
+ if (unlink(path) < 0) {
+ if (errno != -ENOENT && r >= 0)
+ r = -errno;
+ } else {
+ q = mark_symlink_for_removal(&remove_symlinks_to, path);
+ if (q < 0)
+ return q;
+
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
+ }
+ }
+
+ q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes);
+ if (r >= 0)
+ r = q;
+
+ return r;
+}
+
+int unit_file_link(
UnitFileScope scope,
bool runtime,
const char *root_dir,
char **files,
- char *target,
- UnitDependency dep,
bool force,
UnitFileChange **changes,
unsigned *n_changes) {
_cleanup_lookup_paths_free_ LookupPaths paths = {};
- _cleanup_(install_context_done) InstallContext c = {};
_cleanup_free_ char *config_path = NULL;
+ _cleanup_free_ char **todo = NULL;
+ size_t n_todo = 0, n_allocated = 0;
char **i;
- int r;
- UnitFileInstallInfo *info;
+ int r, q;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
+ r = verify_root_dir(scope, &root_dir);
+ if (r < 0)
+ return r;
+
r = lookup_paths_init_from_scope(&paths, scope, root_dir);
if (r < 0)
return r;
@@ -1527,55 +1650,135 @@ int unit_file_add_dependency(
return r;
STRV_FOREACH(i, files) {
- UnitFileState state;
+ _cleanup_free_ char *full = NULL;
+ struct stat st;
+ char *fn;
- state = unit_file_get_state(scope, root_dir, *i);
- if (state < 0)
- return log_error_errno(state, "Failed to get unit file state for %s: %m", *i);
+ if (!path_is_absolute(*i))
+ return -EINVAL;
- if (state == UNIT_FILE_MASKED || state == UNIT_FILE_MASKED_RUNTIME) {
- log_error("Failed to enable unit: Unit %s is masked", *i);
- return -EOPNOTSUPP;
- }
+ fn = basename(*i);
+ if (!unit_name_is_valid(fn, UNIT_NAME_ANY))
+ return -EINVAL;
- r = install_info_add_auto(&c, *i);
- if (r < 0)
- return r;
+ full = prefix_root(root_dir, *i);
+ if (!full)
+ return -ENOMEM;
+
+ if (lstat(full, &st) < 0)
+ return -errno;
+ if (S_ISLNK(st.st_mode))
+ return -ELOOP;
+ if (S_ISDIR(st.st_mode))
+ return -EISDIR;
+ if (!S_ISREG(st.st_mode))
+ return -ENOTTY;
+
+ q = in_search_path(*i, paths.unit_path);
+ if (q < 0)
+ return q;
+ if (q > 0)
+ continue;
+
+ if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
+ return -ENOMEM;
+
+ todo[n_todo++] = *i;
}
- if (!ordered_hashmap_isempty(c.will_install)) {
- r = ordered_hashmap_ensure_allocated(&c.have_installed, &string_hash_ops);
- if (r < 0)
- return r;
+ strv_uniq(todo);
- r = ordered_hashmap_reserve(c.have_installed, ordered_hashmap_size(c.will_install));
- if (r < 0)
- return r;
+ r = 0;
+ STRV_FOREACH(i, todo) {
+ _cleanup_free_ char *path = NULL;
+
+ path = path_make_absolute(basename(*i), config_path);
+ if (!path)
+ return -ENOMEM;
+
+ q = create_symlink(*i, path, force, changes, n_changes);
+ if (q < 0 && r >= 0)
+ r = q;
}
- while ((info = ordered_hashmap_first(c.will_install))) {
- assert_se(ordered_hashmap_move_one(c.have_installed, c.will_install, info->name) == 0);
+ return r;
+}
+
+int unit_file_add_dependency(
+ UnitFileScope scope,
+ bool runtime,
+ const char *root_dir,
+ char **files,
+ const char *target,
+ UnitDependency dep,
+ bool force,
+ UnitFileChange **changes,
+ unsigned *n_changes) {
+
+ _cleanup_lookup_paths_free_ LookupPaths paths = {};
+ _cleanup_(install_context_done) InstallContext c = {};
+ _cleanup_free_ char *config_path = NULL;
+ UnitFileInstallInfo *i, *target_info;
+ char **f;
+ int r;
+
+ assert(scope >= 0);
+ assert(scope < _UNIT_FILE_SCOPE_MAX);
+ assert(target);
+
+ if (!IN_SET(dep, UNIT_WANTS, UNIT_REQUIRES))
+ return -EINVAL;
+
+ if (!unit_name_is_valid(target, UNIT_NAME_ANY))
+ return -EINVAL;
+
+ r = verify_root_dir(scope, &root_dir);
+ if (r < 0)
+ return r;
+
+ r = lookup_paths_init_from_scope(&paths, scope, root_dir);
+ if (r < 0)
+ return r;
+
+ r = get_config_path(scope, runtime, root_dir, &config_path);
+ if (r < 0)
+ return r;
+
+ r = install_info_discover(scope, &c, root_dir, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS, &target_info);
+ if (r < 0)
+ return r;
+ if (target_info->type == UNIT_FILE_TYPE_MASKED)
+ return -ESHUTDOWN;
+
+ assert(target_info->type == UNIT_FILE_TYPE_REGULAR);
- r = unit_file_search(&c, info, &paths, root_dir, false, false, NULL);
+ STRV_FOREACH(f, files) {
+ char ***l;
+
+ r = install_info_discover(scope, &c, root_dir, &paths, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
if (r < 0)
return r;
+ if (i->type == UNIT_FILE_TYPE_MASKED)
+ return -ESHUTDOWN;
+
+ assert(i->type == UNIT_FILE_TYPE_REGULAR);
+
+ /* We didn't actually load anything from the unit
+ * file, but instead just add in our new symlink to
+ * create. */
if (dep == UNIT_WANTS)
- r = strv_extend(&info->wanted_by, target);
- else if (dep == UNIT_REQUIRES)
- r = strv_extend(&info->required_by, target);
+ l = &i->wanted_by;
else
- r = -EINVAL;
-
- if (r < 0)
- return r;
+ l = &i->required_by;
- r = install_info_apply(info, &paths, config_path, root_dir, force, changes, n_changes);
- if (r < 0)
- return r;
+ strv_free(*l);
+ *l = strv_new(target_info->name, NULL);
+ if (!*l)
+ return -ENOMEM;
}
- return 0;
+ return install_context_apply(scope, &c, &paths, config_path, root_dir, force, SEARCH_FOLLOW_CONFIG_SYMLINKS, changes, n_changes);
}
int unit_file_enable(
@@ -1589,13 +1792,18 @@ int unit_file_enable(
_cleanup_lookup_paths_free_ LookupPaths paths = {};
_cleanup_(install_context_done) InstallContext c = {};
- char **i;
_cleanup_free_ char *config_path = NULL;
+ UnitFileInstallInfo *i;
+ char **f;
int r;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
+ r = verify_root_dir(scope, &root_dir);
+ if (r < 0)
+ return r;
+
r = lookup_paths_init_from_scope(&paths, scope, root_dir);
if (r < 0)
return r;
@@ -1604,29 +1812,22 @@ int unit_file_enable(
if (r < 0)
return r;
- STRV_FOREACH(i, files) {
- UnitFileState state;
-
- /* We only want to know if this unit is masked, so we ignore
- * errors from unit_file_get_state, deferring other checks.
- * This allows templated units to be enabled on the fly. */
- 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 -EOPNOTSUPP;
- }
-
- r = install_info_add_auto(&c, *i);
+ STRV_FOREACH(f, files) {
+ r = install_info_discover(scope, &c, root_dir, &paths, *f, SEARCH_LOAD, &i);
if (r < 0)
return r;
+ if (i->type == UNIT_FILE_TYPE_MASKED)
+ return -ESHUTDOWN;
+
+ assert(i->type == UNIT_FILE_TYPE_REGULAR);
}
/* This will return the number of symlink rules that were
- supposed to be created, not the ones actually created. This is
- useful to determine whether the passed files had any
- installation data at all. */
+ supposed to be created, not the ones actually created. This
+ is useful to determine whether the passed files had any
+ installation data at all. */
- return install_context_apply(&c, &paths, config_path, root_dir, force, changes, n_changes);
+ return install_context_apply(scope, &c, &paths, config_path, root_dir, force, SEARCH_LOAD, changes, n_changes);
}
int unit_file_disable(
@@ -1639,14 +1840,18 @@ int unit_file_disable(
_cleanup_lookup_paths_free_ LookupPaths paths = {};
_cleanup_(install_context_done) InstallContext c = {};
- char **i;
_cleanup_free_ char *config_path = NULL;
_cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
- int r, q;
+ char **i;
+ int r;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
+ r = verify_root_dir(scope, &root_dir);
+ if (r < 0)
+ return r;
+
r = lookup_paths_init_from_scope(&paths, scope, root_dir);
if (r < 0)
return r;
@@ -1656,18 +1861,19 @@ int unit_file_disable(
return r;
STRV_FOREACH(i, files) {
- r = install_info_add_auto(&c, *i);
+ if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
+ return -EINVAL;
+
+ r = install_info_add(&c, *i, NULL, NULL);
if (r < 0)
return r;
}
- r = install_context_mark_for_removal(&c, &paths, &remove_symlinks_to, config_path, root_dir);
-
- q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files);
- if (r >= 0)
- r = q;
+ r = install_context_mark_for_removal(scope, &c, &paths, &remove_symlinks_to, config_path, root_dir);
+ if (r < 0)
+ return r;
- return r;
+ return remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes);
}
int unit_file_reenable(
@@ -1678,21 +1884,30 @@ int unit_file_reenable(
bool force,
UnitFileChange **changes,
unsigned *n_changes) {
+
+ char **n;
int r;
+ size_t l, i;
+
+ /* First, we invoke the disable command with only the basename... */
+ l = strv_length(files);
+ n = newa(char*, l+1);
+ for (i = 0; i < l; i++)
+ n[i] = basename(files[i]);
+ n[i] = NULL;
- r = unit_file_disable(scope, runtime, root_dir, files,
- changes, n_changes);
+ r = unit_file_disable(scope, runtime, root_dir, n, changes, n_changes);
if (r < 0)
return r;
- return unit_file_enable(scope, runtime, root_dir, files, force,
- changes, n_changes);
+ /* But the enable command with the full name */
+ return unit_file_enable(scope, runtime, root_dir, files, force, changes, n_changes);
}
int unit_file_set_default(
UnitFileScope scope,
const char *root_dir,
- const char *file,
+ const char *name,
bool force,
UnitFileChange **changes,
unsigned *n_changes) {
@@ -1700,42 +1915,40 @@ int unit_file_set_default(
_cleanup_lookup_paths_free_ LookupPaths paths = {};
_cleanup_(install_context_done) InstallContext c = {};
_cleanup_free_ char *config_path = NULL;
- char *path;
+ UnitFileInstallInfo *i;
+ const char *path;
int r;
- UnitFileInstallInfo *i = NULL;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
- assert(file);
+ assert(name);
- if (unit_name_to_type(file) != UNIT_TARGET)
+ if (unit_name_to_type(name) != UNIT_TARGET)
+ return -EINVAL;
+ if (streq(name, SPECIAL_DEFAULT_TARGET))
return -EINVAL;
- r = lookup_paths_init_from_scope(&paths, scope, root_dir);
+ r = verify_root_dir(scope, &root_dir);
if (r < 0)
return r;
- r = get_config_path(scope, false, root_dir, &config_path);
+ r = lookup_paths_init_from_scope(&paths, scope, root_dir);
if (r < 0)
return r;
- r = install_info_add_auto(&c, file);
+ r = get_config_path(scope, false, root_dir, &config_path);
if (r < 0)
return r;
- assert_se(i = ordered_hashmap_first(c.will_install));
-
- r = unit_file_search(&c, i, &paths, root_dir, false, true, NULL);
+ r = install_info_discover(scope, &c, root_dir, &paths, name, 0, &i);
if (r < 0)
return r;
+ if (i->type == UNIT_FILE_TYPE_MASKED)
+ return -ESHUTDOWN;
path = strjoina(config_path, "/" SPECIAL_DEFAULT_TARGET);
- r = create_symlink(i->path, path, force, changes, n_changes);
- if (r < 0)
- return r;
-
- return 0;
+ return create_symlink(i->path, path, force, changes, n_changes);
}
int unit_file_get_default(
@@ -1744,126 +1957,101 @@ int unit_file_get_default(
char **name) {
_cleanup_lookup_paths_free_ LookupPaths paths = {};
- char **p;
+ _cleanup_(install_context_done) InstallContext c = {};
+ UnitFileInstallInfo *i;
+ char *n;
int r;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
assert(name);
- r = lookup_paths_init_from_scope(&paths, scope, root_dir);
+ r = verify_root_dir(scope, &root_dir);
if (r < 0)
return r;
- STRV_FOREACH(p, paths.unit_path) {
- _cleanup_free_ char *path = NULL, *tmp = NULL;
- char *n;
-
- path = path_join(root_dir, *p, SPECIAL_DEFAULT_TARGET);
- if (!path)
- return -ENOMEM;
-
- r = readlink_malloc(path, &tmp);
- if (r == -ENOENT)
- continue;
- else if (r == -EINVAL)
- /* not a symlink */
- n = strdup(SPECIAL_DEFAULT_TARGET);
- else if (r < 0)
- return r;
- else
- n = strdup(basename(tmp));
+ r = lookup_paths_init_from_scope(&paths, scope, root_dir);
+ if (r < 0)
+ return r;
- if (!n)
- return -ENOMEM;
+ r = install_info_discover(scope, &c, root_dir, &paths, SPECIAL_DEFAULT_TARGET, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
+ if (r < 0)
+ return r;
+ if (i->type == UNIT_FILE_TYPE_MASKED)
+ return -ESHUTDOWN;
- *name = n;
- return 0;
- }
+ n = strdup(i->name);
+ if (!n)
+ return -ENOMEM;
- return -ENOENT;
+ *name = n;
+ return 0;
}
-UnitFileState unit_file_lookup_state(
+int unit_file_lookup_state(
UnitFileScope scope,
const char *root_dir,
const LookupPaths *paths,
- const char *name) {
+ const char *name,
+ UnitFileState *ret) {
- UnitFileState state = _UNIT_FILE_STATE_INVALID;
- char **i;
- _cleanup_free_ char *path = NULL;
- int r = 0;
+ _cleanup_(install_context_done) InstallContext c = {};
+ UnitFileInstallInfo *i;
+ UnitFileState state;
+ int r;
assert(paths);
+ assert(name);
if (!unit_name_is_valid(name, UNIT_NAME_ANY))
return -EINVAL;
- STRV_FOREACH(i, paths->unit_path) {
- struct stat st;
- char *partial;
- bool also = false;
+ r = verify_root_dir(scope, &root_dir);
+ if (r < 0)
+ return r;
- free(path);
- path = path_join(root_dir, *i, name);
- if (!path)
- return -ENOMEM;
+ r = install_info_discover(scope, &c, root_dir, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
+ if (r < 0)
+ return r;
- if (root_dir)
- partial = path + strlen(root_dir);
- else
- partial = path;
-
- /*
- * Search for a unit file in our default paths, to
- * be sure, that there are no broken symlinks.
- */
- if (lstat(path, &st) < 0) {
- r = -errno;
- if (errno != ENOENT)
- return r;
+ /* Shortcut things, if the caller just wants to know if this unit exists. */
+ if (!ret)
+ return 0;
- if (!unit_name_is_valid(name, UNIT_NAME_INSTANCE))
- continue;
- } else {
- if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))
- return -ENOENT;
+ switch (i->type) {
- r = null_or_empty_path(path);
- if (r < 0 && r != -ENOENT)
- return r;
- else if (r > 0) {
- state = path_startswith(*i, "/run") ? UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
- return state;
- }
- }
+ case UNIT_FILE_TYPE_MASKED:
+ state = path_startswith(i->path, "/run") ? UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
+ break;
- r = find_symlinks_in_scope(scope, root_dir, name, &state);
+ case UNIT_FILE_TYPE_REGULAR:
+ r = find_symlinks_in_scope(scope, root_dir, i->name, &state);
if (r < 0)
return r;
- else if (r > 0)
- return state;
-
- r = unit_file_can_install(paths, root_dir, partial, true, &also);
- if (r < 0 && errno != ENOENT)
- return r;
- else if (r > 0)
- return UNIT_FILE_DISABLED;
- else if (r == 0) {
- if (also)
- return UNIT_FILE_INDIRECT;
- return UNIT_FILE_STATIC;
+ if (r == 0) {
+ if (UNIT_FILE_INSTALL_INFO_HAS_RULES(i))
+ state = UNIT_FILE_DISABLED;
+ else if (UNIT_FILE_INSTALL_INFO_HAS_ALSO(i))
+ state = UNIT_FILE_INDIRECT;
+ else
+ state = UNIT_FILE_STATIC;
}
+
+ break;
+
+ default:
+ assert_not_reached("Unexpect unit file type.");
}
- return r < 0 ? r : state;
+ *ret = state;
+ return 0;
}
-UnitFileState unit_file_get_state(
+int unit_file_get_state(
UnitFileScope scope,
const char *root_dir,
- const char *name) {
+ const char *name,
+ UnitFileState *ret) {
_cleanup_lookup_paths_free_ LookupPaths paths = {};
int r;
@@ -1872,14 +2060,15 @@ UnitFileState unit_file_get_state(
assert(scope < _UNIT_FILE_SCOPE_MAX);
assert(name);
- if (root_dir && scope != UNIT_FILE_SYSTEM)
- return -EINVAL;
+ r = verify_root_dir(scope, &root_dir);
+ if (r < 0)
+ return r;
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);
+ return unit_file_lookup_state(scope, root_dir, &paths, name, ret);
}
int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) {
@@ -1891,6 +2080,13 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char
assert(scope < _UNIT_FILE_SCOPE_MAX);
assert(name);
+ r = verify_root_dir(scope, &root_dir);
+ if (r < 0)
+ return r;
+
+ if (!unit_name_is_valid(name, UNIT_NAME_ANY))
+ return -EINVAL;
+
if (scope == UNIT_FILE_SYSTEM)
r = conf_files_list(&files, ".preset", root_dir,
"/etc/systemd/system-preset",
@@ -1907,13 +2103,14 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char
"/usr/lib/systemd/user-preset",
NULL);
else
- return 1;
+ return 1; /* Default is "enable" */
if (r < 0)
return r;
STRV_FOREACH(p, files) {
_cleanup_fclose_ FILE *f;
+ char line[LINE_MAX];
f = fopen(*p, "re");
if (!f) {
@@ -1923,39 +2120,38 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char
return -errno;
}
- for (;;) {
- char line[LINE_MAX], *l;
-
- if (!fgets(line, sizeof(line), f))
- break;
+ FOREACH_LINE(line, f, return -errno) {
+ const char *parameter;
+ char *l;
l = strstrip(line);
- if (!*l)
- continue;
- if (strchr(COMMENTS "\n", *l))
+ if (isempty(l))
+ continue;
+ if (strchr(COMMENTS, *l))
continue;
- if (first_word(l, "enable")) {
- l += 6;
- l += strspn(l, WHITESPACE);
-
- if (fnmatch(l, name, FNM_NOESCAPE) == 0) {
+ parameter = first_word(l, "enable");
+ if (parameter) {
+ if (fnmatch(parameter, name, FNM_NOESCAPE) == 0) {
log_debug("Preset file says enable %s.", name);
return 1;
}
- } else if (first_word(l, "disable")) {
- l += 7;
- l += strspn(l, WHITESPACE);
+ continue;
+ }
- if (fnmatch(l, name, FNM_NOESCAPE) == 0) {
+ parameter = first_word(l, "disable");
+ if (parameter) {
+ if (fnmatch(parameter, name, FNM_NOESCAPE) == 0) {
log_debug("Preset file says disable %s.", name);
return 0;
}
- } else
- log_debug("Couldn't parse line '%s'", l);
+ continue;
+ }
+
+ log_debug("Couldn't parse line '%s'", l);
}
}
@@ -1964,6 +2160,86 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char
return 1;
}
+static int execute_preset(
+ UnitFileScope scope,
+ InstallContext *plus,
+ InstallContext *minus,
+ const LookupPaths *paths,
+ const char *config_path,
+ const char *root_dir,
+ char **files,
+ UnitFilePresetMode mode,
+ bool force,
+ UnitFileChange **changes,
+ unsigned *n_changes) {
+
+ int r;
+
+ assert(plus);
+ assert(minus);
+ assert(paths);
+ assert(config_path);
+
+ if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) {
+ _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
+
+ r = install_context_mark_for_removal(scope, minus, paths, &remove_symlinks_to, config_path, root_dir);
+ if (r < 0)
+ return r;
+
+ r = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes);
+ } else
+ r = 0;
+
+ if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) {
+ int q;
+
+ /* Returns number of symlinks that where supposed to be installed. */
+ q = install_context_apply(scope, plus, paths, config_path, root_dir, force, SEARCH_LOAD, changes, n_changes);
+ if (r >= 0) {
+ if (q < 0)
+ r = q;
+ else
+ r+= q;
+ }
+ }
+
+ return r;
+}
+
+static int preset_prepare_one(
+ UnitFileScope scope,
+ InstallContext *plus,
+ InstallContext *minus,
+ LookupPaths *paths,
+ const char *root_dir,
+ UnitFilePresetMode mode,
+ const char *name) {
+
+ UnitFileInstallInfo *i;
+ int r;
+
+ if (install_info_find(plus, name) ||
+ install_info_find(minus, name))
+ return 0;
+
+ r = unit_file_query_preset(scope, root_dir, name);
+ if (r < 0)
+ return r;
+
+ if (r > 0) {
+ r = install_info_discover(scope, plus, root_dir, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
+ if (r < 0)
+ return r;
+
+ if (i->type == UNIT_FILE_TYPE_MASKED)
+ return -ESHUTDOWN;
+ } else
+ r = install_info_discover(scope, minus, root_dir, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
+
+ return r;
+}
+
int unit_file_preset(
UnitFileScope scope,
bool runtime,
@@ -1978,12 +2254,16 @@ int unit_file_preset(
_cleanup_lookup_paths_free_ LookupPaths paths = {};
_cleanup_free_ char *config_path = NULL;
char **i;
- int r, q;
+ int r;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
assert(mode < _UNIT_FILE_PRESET_MAX);
+ r = verify_root_dir(scope, &root_dir);
+ if (r < 0)
+ return r;
+
r = lookup_paths_init_from_scope(&paths, scope, root_dir);
if (r < 0)
return r;
@@ -1993,44 +2273,15 @@ int unit_file_preset(
return r;
STRV_FOREACH(i, files) {
-
if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
return -EINVAL;
- r = unit_file_query_preset(scope, root_dir, *i);
- if (r < 0)
- return r;
-
- if (r && mode != UNIT_FILE_PRESET_DISABLE_ONLY)
- r = install_info_add_auto(&plus, *i);
- else if (!r && mode != UNIT_FILE_PRESET_ENABLE_ONLY)
- r = install_info_add_auto(&minus, *i);
- else
- r = 0;
+ r = preset_prepare_one(scope, &plus, &minus, &paths, root_dir, mode, *i);
if (r < 0)
return r;
}
- r = 0;
-
- if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) {
- _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
-
- r = install_context_mark_for_removal(&minus, &paths, &remove_symlinks_to, config_path, root_dir);
-
- q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files);
- if (r == 0)
- r = q;
- }
-
- if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) {
- /* Returns number of symlinks that where supposed to be installed. */
- q = install_context_apply(&plus, &paths, config_path, root_dir, force, changes, n_changes);
- if (r == 0)
- r = q;
- }
-
- return r;
+ return execute_preset(scope, &plus, &minus, &paths, config_path, root_dir, files, mode, force, changes, n_changes);
}
int unit_file_preset_all(
@@ -2046,12 +2297,16 @@ int unit_file_preset_all(
_cleanup_lookup_paths_free_ LookupPaths paths = {};
_cleanup_free_ char *config_path = NULL;
char **i;
- int r, q;
+ int r;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
assert(mode < _UNIT_FILE_PRESET_MAX);
+ r = verify_root_dir(scope, &root_dir);
+ if (r < 0)
+ return r;
+
r = lookup_paths_init_from_scope(&paths, scope, root_dir);
if (r < 0)
return r;
@@ -2063,6 +2318,7 @@ int unit_file_preset_all(
STRV_FOREACH(i, paths.unit_path) {
_cleanup_closedir_ DIR *d = NULL;
_cleanup_free_ char *units_dir;
+ struct dirent *de;
units_dir = path_join(root_dir, *i, NULL);
if (!units_dir)
@@ -2076,62 +2332,23 @@ int unit_file_preset_all(
return -errno;
}
- for (;;) {
- struct dirent *de;
-
- errno = 0;
- de = readdir(d);
- if (!de && errno != 0)
- return -errno;
-
- if (!de)
- break;
-
- if (hidden_file(de->d_name))
- continue;
+ FOREACH_DIRENT(de, d, return -errno) {
if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
continue;
dirent_ensure_type(d, de);
- if (de->d_type != DT_REG)
+ if (!IN_SET(de->d_type, DT_LNK, DT_REG))
continue;
- r = unit_file_query_preset(scope, root_dir, de->d_name);
- if (r < 0)
- return r;
-
- if (r && mode != UNIT_FILE_PRESET_DISABLE_ONLY)
- r = install_info_add_auto(&plus, de->d_name);
- else if (!r && mode != UNIT_FILE_PRESET_ENABLE_ONLY)
- r = install_info_add_auto(&minus, de->d_name);
- else
- r = 0;
+ r = preset_prepare_one(scope, &plus, &minus, &paths, root_dir, mode, de->d_name);
if (r < 0)
return r;
}
}
- r = 0;
-
- if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) {
- _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
-
- r = install_context_mark_for_removal(&minus, &paths, &remove_symlinks_to, config_path, root_dir);
-
- q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, NULL);
- if (r == 0)
- r = q;
- }
-
- if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) {
- q = install_context_apply(&plus, &paths, config_path, root_dir, force, changes, n_changes);
- if (r == 0)
- r = q;
- }
-
- return r;
+ return execute_preset(scope, &plus, &minus, &paths, config_path, root_dir, NULL, mode, force, changes, n_changes);
}
static void unit_file_list_free_one(UnitFileList *f) {
@@ -2142,6 +2359,15 @@ static void unit_file_list_free_one(UnitFileList *f) {
free(f);
}
+Hashmap* unit_file_list_free(Hashmap *h) {
+ UnitFileList *i;
+
+ while ((i = hashmap_steal_first(h)))
+ unit_file_list_free_one(i);
+
+ return hashmap_free(h);
+}
+
DEFINE_TRIVIAL_CLEANUP_FUNC(UnitFileList*, unit_file_list_free_one);
int unit_file_get_list(
@@ -2157,14 +2383,9 @@ int unit_file_get_list(
assert(scope < _UNIT_FILE_SCOPE_MAX);
assert(h);
- if (root_dir && scope != UNIT_FILE_SYSTEM)
- return -EINVAL;
-
- if (root_dir) {
- r = access(root_dir, F_OK);
- if (r < 0)
- return -errno;
- }
+ r = verify_root_dir(scope, &root_dir);
+ if (r < 0)
+ return r;
r = lookup_paths_init_from_scope(&paths, scope, root_dir);
if (r < 0)
@@ -2173,6 +2394,7 @@ int unit_file_get_list(
STRV_FOREACH(i, paths.unit_path) {
_cleanup_closedir_ DIR *d = NULL;
_cleanup_free_ char *units_dir;
+ struct dirent *de;
units_dir = path_join(root_dir, *i, NULL);
if (!units_dir)
@@ -2186,22 +2408,8 @@ int unit_file_get_list(
return -errno;
}
- for (;;) {
+ FOREACH_DIRENT(de, d, return -errno) {
_cleanup_(unit_file_list_free_onep) UnitFileList *f = NULL;
- struct dirent *de;
- _cleanup_free_ char *path = NULL;
- bool also = false;
-
- errno = 0;
- de = readdir(d);
- if (!de && errno != 0)
- return -errno;
-
- if (!de)
- break;
-
- if (hidden_file(de->d_name))
- continue;
if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
continue;
@@ -2222,44 +2430,14 @@ int unit_file_get_list(
if (!f->path)
return -ENOMEM;
- r = null_or_empty_path(f->path);
- if (r < 0 && r != -ENOENT)
- return r;
- else if (r > 0) {
- f->state =
- path_startswith(*i, "/run") ?
- UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
- goto found;
- }
-
- r = find_symlinks_in_scope(scope, root_dir, de->d_name, &f->state);
+ r = unit_file_lookup_state(scope, root_dir, &paths, basename(f->path), &f->state);
if (r < 0)
- return r;
- else if (r > 0) {
- f->state = UNIT_FILE_ENABLED;
- goto found;
- }
-
- path = path_make_absolute(de->d_name, *i);
- if (!path)
- return -ENOMEM;
+ f->state = UNIT_FILE_BAD;
- r = unit_file_can_install(&paths, root_dir, path, true, &also);
- if (r == -EINVAL || /* Invalid setting? */
- r == -EBADMSG || /* Invalid format? */
- r == -ENOENT /* Included file not found? */)
- f->state = UNIT_FILE_INVALID;
- else if (r < 0)
- return r;
- else if (r > 0)
- f->state = UNIT_FILE_DISABLED;
- else
- f->state = also ? UNIT_FILE_INDIRECT : UNIT_FILE_STATIC;
-
- found:
r = hashmap_put(h, basename(f->path), f);
if (r < 0)
return r;
+
f = NULL; /* prevent cleanup */
}
}
@@ -2277,7 +2455,7 @@ static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = {
[UNIT_FILE_STATIC] = "static",
[UNIT_FILE_DISABLED] = "disabled",
[UNIT_FILE_INDIRECT] = "indirect",
- [UNIT_FILE_INVALID] = "invalid",
+ [UNIT_FILE_BAD] = "bad",
};
DEFINE_STRING_TABLE_LOOKUP(unit_file_state, UnitFileState);
diff --git a/src/shared/install.h b/src/shared/install.h
index a9d77dd91b..5519fbcf8f 100644
--- a/src/shared/install.h
+++ b/src/shared/install.h
@@ -25,13 +25,18 @@ typedef enum UnitFileScope UnitFileScope;
typedef enum UnitFileState UnitFileState;
typedef enum UnitFilePresetMode UnitFilePresetMode;
typedef enum UnitFileChangeType UnitFileChangeType;
+typedef enum UnitFileType UnitFileType;
typedef struct UnitFileChange UnitFileChange;
typedef struct UnitFileList UnitFileList;
typedef struct UnitFileInstallInfo UnitFileInstallInfo;
+#include <stdbool.h>
+
#include "hashmap.h"
-#include "unit-name.h"
+#include "macro.h"
#include "path-lookup.h"
+#include "strv.h"
+#include "unit-name.h"
enum UnitFileScope {
UNIT_FILE_SYSTEM,
@@ -51,7 +56,7 @@ enum UnitFileState {
UNIT_FILE_STATIC,
UNIT_FILE_DISABLED,
UNIT_FILE_INDIRECT,
- UNIT_FILE_INVALID,
+ UNIT_FILE_BAD,
_UNIT_FILE_STATE_MAX,
_UNIT_FILE_STATE_INVALID = -1
};
@@ -82,10 +87,17 @@ struct UnitFileList {
UnitFileState state;
};
+enum UnitFileType {
+ UNIT_FILE_TYPE_REGULAR,
+ UNIT_FILE_TYPE_SYMLINK,
+ UNIT_FILE_TYPE_MASKED,
+ _UNIT_FILE_TYPE_MAX,
+ _UNIT_FILE_TYPE_INVALID = -1,
+};
+
struct UnitFileInstallInfo {
char *name;
char *path;
- char *user;
char **aliases;
char **wanted_by;
@@ -93,8 +105,26 @@ struct UnitFileInstallInfo {
char **also;
char *default_instance;
+
+ UnitFileType type;
+
+ char *symlink_target;
};
+static inline bool UNIT_FILE_INSTALL_INFO_HAS_RULES(UnitFileInstallInfo *i) {
+ assert(i);
+
+ return !strv_isempty(i->aliases) ||
+ !strv_isempty(i->wanted_by) ||
+ !strv_isempty(i->required_by);
+}
+
+static inline bool UNIT_FILE_INSTALL_INFO_HAS_ALSO(UnitFileInstallInfo *i) {
+ assert(i);
+
+ return !strv_isempty(i->also);
+}
+
int unit_file_enable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes);
int unit_file_disable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, UnitFileChange **changes, unsigned *n_changes);
int unit_file_reenable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes);
@@ -105,21 +135,14 @@ int unit_file_mask(UnitFileScope scope, bool runtime, const char *root_dir, char
int unit_file_unmask(UnitFileScope scope, bool runtime, const char *root_dir, char **files, UnitFileChange **changes, unsigned *n_changes);
int unit_file_set_default(UnitFileScope scope, const char *root_dir, const char *file, bool force, UnitFileChange **changes, unsigned *n_changes);
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_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_add_dependency(UnitFileScope scope, bool runtime, const char *root_dir, char **files, const char *target, UnitDependency dep, bool force, UnitFileChange **changes, unsigned *n_changes);
+
+int unit_file_lookup_state(UnitFileScope scope, const char *root_dir,const LookupPaths *paths, const char *name, UnitFileState *ret);
+int unit_file_get_state(UnitFileScope scope, const char *root_dir, const char *filename, UnitFileState *ret);
int unit_file_get_list(UnitFileScope scope, const char *root_dir, Hashmap *h);
+Hashmap* unit_file_list_free(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);
diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c
index dbc07aa7ad..a1f65d1a88 100644
--- a/src/shared/logs-show.c
+++ b/src/shared/logs-show.c
@@ -19,25 +19,42 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <time.h>
#include <errno.h>
-#include <sys/socket.h>
-#include <string.h>
#include <fcntl.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
-#include "logs-show.h"
-#include "log.h"
-#include "util.h"
-#include "utf8.h"
+#include "sd-id128.h"
+#include "sd-journal.h"
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "formats-util.h"
#include "hashmap.h"
+#include "hostname-util.h"
+#include "io-util.h"
#include "journal-internal.h"
-#include "formats-util.h"
+#include "log.h"
+#include "logs-show.h"
+#include "macro.h"
+#include "output-mode.h"
+#include "parse-util.h"
#include "process-util.h"
+#include "sparse-endian.h"
+#include "string-table.h"
+#include "string-util.h"
#include "terminal-util.h"
-#include "hostname-util.h"
+#include "time-util.h"
+#include "utf8.h"
+#include "util.h"
-/* up to three lines (each up to 100 characters),
- or 300 characters, whichever is less */
+/* up to three lines (each up to 100 characters) or 300 characters, whichever is less */
#define PRINT_LINE_THRESHOLD 3
#define PRINT_CHAR_THRESHOLD 300
@@ -418,8 +435,9 @@ static int output_verbose(
r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &value, &size);
if (r < 0)
- log_debug_errno(r, "_SOURCE_REALTIME_TIMESTAMP invalid: %m");
+ return r;
else {
+ assert(r > 0);
r = safe_atou64(value, &realtime);
if (r < 0)
log_debug_errno(r, "Failed to parse realtime timestamp: %m");
@@ -1236,7 +1254,7 @@ int show_journal_by_unit(
bool system_unit,
bool *ellipsized) {
- _cleanup_journal_close_ sd_journal*j = NULL;
+ _cleanup_(sd_journal_closep) sd_journal *j = NULL;
int r;
assert(mode >= 0);
diff --git a/src/shared/logs-show.h b/src/shared/logs-show.h
index 569e1faa55..396050936d 100644
--- a/src/shared/logs-show.h
+++ b/src/shared/logs-show.h
@@ -22,12 +22,16 @@
***/
#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
#include <sys/types.h>
#include "sd-journal.h"
-#include "util.h"
+#include "macro.h"
#include "output-mode.h"
+#include "time-util.h"
+#include "util.h"
int output_journal(
FILE *f,
diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c
index 9c1e4d5e13..2ded0ff698 100644
--- a/src/shared/machine-image.c
+++ b/src/shared/machine-image.c
@@ -19,19 +19,37 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <dirent.h>
+#include <errno.h>
#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
#include <linux/fs.h>
-#include <sys/statfs.h>
-
+#include "alloc-util.h"
#include "btrfs-util.h"
+#include "chattr-util.h"
#include "copy.h"
+#include "dirent-util.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "hashmap.h"
+#include "lockfile-util.h"
+#include "log.h"
+#include "macro.h"
+#include "machine-image.h"
#include "mkdir.h"
#include "path-util.h"
#include "rm-rf.h"
+#include "string-table.h"
+#include "string-util.h"
#include "strv.h"
+#include "time-util.h"
#include "utf8.h"
-
-#include "machine-image.h"
+#include "util.h"
+#include "xattr-util.h"
static const char image_search_path[] =
"/var/lib/machines\0"
@@ -176,11 +194,10 @@ static int image_make(
return r;
if (r) {
BtrfsSubvolInfo info;
- BtrfsQuotaInfo quota;
/* It's a btrfs subvolume */
- r = btrfs_subvol_get_info_fd(fd, &info);
+ r = btrfs_subvol_get_info_fd(fd, 0, &info);
if (r < 0)
return r;
@@ -195,13 +212,17 @@ static int image_make(
if (r < 0)
return r;
- r = btrfs_subvol_get_quota_fd(fd, &quota);
- if (r >= 0) {
- (*ret)->usage = quota.referenced;
- (*ret)->usage_exclusive = quota.exclusive;
+ if (btrfs_quota_scan_ongoing(fd) == 0) {
+ BtrfsQuotaInfo quota;
+
+ r = btrfs_subvol_get_subtree_quota_fd(fd, 0, &quota);
+ if (r >= 0) {
+ (*ret)->usage = quota.referenced;
+ (*ret)->usage_exclusive = quota.exclusive;
- (*ret)->limit = quota.referenced_max;
- (*ret)->limit_exclusive = quota.exclusive_max;
+ (*ret)->limit = quota.referenced_max;
+ (*ret)->limit_exclusive = quota.exclusive_max;
+ }
}
return 1;
@@ -397,7 +418,7 @@ int image_remove(Image *i) {
switch (i->type) {
case IMAGE_SUBVOLUME:
- r = btrfs_subvol_remove(i->path, true);
+ r = btrfs_subvol_remove(i->path, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA);
if (r < 0)
return r;
break;
@@ -587,7 +608,12 @@ 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 ? BTRFS_SNAPSHOT_READ_ONLY : 0) | BTRFS_SNAPSHOT_FALLBACK_COPY | BTRFS_SNAPSHOT_RECURSIVE);
+ r = btrfs_subvol_snapshot(i->path, new_path, (read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | BTRFS_SNAPSHOT_FALLBACK_COPY | BTRFS_SNAPSHOT_RECURSIVE | BTRFS_SNAPSHOT_QUOTA);
+
+ /* Enable "subtree" quotas for the copy, if we didn't
+ * copy any quota from the source. */
+ (void) btrfs_subvol_auto_qgroup(i->path, 0, true);
+
break;
case IMAGE_RAW:
@@ -629,6 +655,10 @@ int image_read_only(Image *i, bool b) {
switch (i->type) {
case IMAGE_SUBVOLUME:
+
+ /* Note that we set the flag only on the top-level
+ * subvolume of the image. */
+
r = btrfs_subvol_set_read_only(i->path, b);
if (r < 0)
return r;
@@ -729,7 +759,14 @@ int image_set_limit(Image *i, uint64_t referenced_max) {
if (i->type != IMAGE_SUBVOLUME)
return -EOPNOTSUPP;
- return btrfs_quota_limit(i->path, referenced_max);
+ /* We set the quota both for the subvolume as well as for the
+ * subtree. The latter is mostly for historical reasons, since
+ * we didn't use to have a concept of subtree quota, and hence
+ * only modified the subvolume quota. */
+
+ (void) btrfs_qgroup_set_limit(i->path, 0, referenced_max);
+ (void) btrfs_subvol_auto_qgroup(i->path, 0, true);
+ return btrfs_subvol_set_subtree_quota_limit(i->path, 0, referenced_max);
}
int image_name_lock(const char *name, int operation, LockFile *ret) {
diff --git a/src/shared/machine-image.h b/src/shared/machine-image.h
index f041600fbf..5e9d8f6980 100644
--- a/src/shared/machine-image.h
+++ b/src/shared/machine-image.h
@@ -21,9 +21,13 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "time-util.h"
-#include "lockfile-util.h"
+#include <stdbool.h>
+#include <stdint.h>
+
#include "hashmap.h"
+#include "lockfile-util.h"
+#include "macro.h"
+#include "time-util.h"
typedef enum ImageType {
IMAGE_DIRECTORY,
diff --git a/src/shared/machine-pool.c b/src/shared/machine-pool.c
index 8af78f47d5..23cbd8d600 100644
--- a/src/shared/machine-pool.c
+++ b/src/shared/machine-pool.c
@@ -19,19 +19,42 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/loop.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
#include <sys/prctl.h>
-#include <sys/vfs.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
#include <sys/statvfs.h>
-#include <sys/mount.h>
+#include <unistd.h>
-#include "util.h"
-#include "process-util.h"
+#include "sd-bus-protocol.h"
+#include "sd-bus.h"
+
+#include "alloc-util.h"
+#include "btrfs-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
#include "lockfile-util.h"
+#include "log.h"
+#include "machine-pool.h"
+#include "macro.h"
+#include "missing.h"
#include "mkdir.h"
-#include "btrfs-util.h"
+#include "mount-util.h"
+#include "parse-util.h"
#include "path-util.h"
+#include "process-util.h"
#include "signal-util.h"
-#include "machine-pool.h"
+#include "stat-util.h"
+#include "string-util.h"
#define VAR_LIB_MACHINES_SIZE_START (1024UL*1024UL*500UL)
#define VAR_LIB_MACHINES_FREE_MIN (1024UL*1024UL*750UL)
@@ -170,7 +193,7 @@ int setup_machine_directory(uint64_t size, sd_bus_error *error) {
};
_cleanup_close_ int fd = -1, control = -1, loop = -1;
_cleanup_free_ char* loopdev = NULL;
- char tmpdir[] = "/tmp/import-mount.XXXXXX", *mntdir = NULL;
+ char tmpdir[] = "/tmp/machine-pool.XXXXXX", *mntdir = NULL;
bool tmpdir_made = false, mntdir_made = false, mntdir_mounted = false;
char buf[FORMAT_BYTES_MAX];
int r, nr = -1;
@@ -194,14 +217,35 @@ int setup_machine_directory(uint64_t size, sd_bus_error *error) {
r = btrfs_quota_enable("/var/lib/machines", true);
if (r < 0)
- log_warning_errno(r, "Failed to enable quota, ignoring: %m");
+ log_warning_errno(r, "Failed to enable quota for /var/lib/machines, ignoring: %m");
+
+ r = btrfs_subvol_auto_qgroup("/var/lib/machines", 0, true);
+ if (r < 0)
+ log_warning_errno(r, "Failed to set up default quota hierarchy for /var/lib/machines, ignoring: %m");
+
+ return 1;
+ }
+
+ if (path_is_mount_point("/var/lib/machines", AT_SYMLINK_FOLLOW) > 0) {
+ log_debug("/var/lib/machines is already a mount point, not creating loopback file for it.");
+ return 0;
+ }
+ r = dir_is_populated("/var/lib/machines");
+ if (r < 0 && r != -ENOENT)
+ return r;
+ if (r > 0) {
+ log_debug("/var/log/machines is already populated, not creating loopback file for it.");
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.");
+ r = mkfs_exists("btrfs");
+ if (r == -ENOENT) {
+ log_debug("mkfs.btrfs is missing, cannot create loopback file for /var/lib/machines.");
+ return 0;
+ }
+ if (r < 0)
+ return r;
fd = setup_machine_raw(size, error);
if (fd < 0)
@@ -266,6 +310,10 @@ int setup_machine_directory(uint64_t size, sd_bus_error *error) {
if (r < 0)
log_warning_errno(r, "Failed to enable quota, ignoring: %m");
+ r = btrfs_subvol_auto_qgroup(mntdir, 0, true);
+ if (r < 0)
+ log_warning_errno(r, "Failed to set up default quota hierarchy, ignoring: %m");
+
if (chmod(mntdir, 0700) < 0) {
r = sd_bus_error_set_errnof(error, errno, "Failed to fix owner: %m");
goto fail;
@@ -286,7 +334,7 @@ int setup_machine_directory(uint64_t size, sd_bus_error *error) {
(void) rmdir(mntdir);
(void) rmdir(tmpdir);
- return 0;
+ return 1;
fail:
if (mntdir_mounted)
@@ -345,7 +393,7 @@ int grow_machine_directory(void) {
if (b.f_bavail > b.f_blocks / 3)
return 0;
- /* Calculate how much we are willing to add at maximum */
+ /* Calculate how much we are willing to add at most */
max_add = ((uint64_t) a.f_bavail * (uint64_t) a.f_bsize) - VAR_LIB_MACHINES_FREE_MIN;
/* Calculate the old size */
@@ -370,9 +418,11 @@ int grow_machine_directory(void) {
if (r <= 0)
return r;
- r = btrfs_quota_limit("/var/lib/machines", new_size);
- if (r < 0)
- return r;
+ /* Also bump the quota, of both the subvolume leaf qgroup, as
+ * well as of any subtree quota group by the same id but a
+ * higher level, if it exists. */
+ (void) btrfs_qgroup_set_limit("/var/lib/machines", 0, new_size);
+ (void) btrfs_subvol_set_subtree_quota_limit("/var/lib/machines", 0, new_size);
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
index fe01d3d47c..a1f2c5c626 100644
--- a/src/shared/machine-pool.h
+++ b/src/shared/machine-pool.h
@@ -21,6 +21,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <stdint.h>
+
#include "sd-bus.h"
/* Grow the /var/lib/machines directory after each 10MiB written */
diff --git a/src/shared/pager.c b/src/shared/pager.c
index 41da820938..07ce926d75 100644
--- a/src/shared/pager.c
+++ b/src/shared/pager.c
@@ -19,19 +19,26 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
#include <stdlib.h>
-#include <unistd.h>
#include <string.h>
#include <sys/prctl.h>
+#include <unistd.h>
+#include "copy.h"
+#include "fd-util.h"
+#include "locale-util.h"
+#include "log.h"
+#include "macro.h"
#include "pager.h"
-#include "util.h"
#include "process-util.h"
-#include "macro.h"
-#include "terminal-util.h"
#include "signal-util.h"
-#include "copy.h"
+#include "string-util.h"
+#include "terminal-util.h"
static pid_t pager_pid = 0;
@@ -48,24 +55,27 @@ noreturn static void pager_fallback(void) {
}
int pager_open(bool jump_to_end) {
- int fd[2];
+ _cleanup_close_pair_ int fd[2] = { -1, -1 };
const char *pager;
pid_t parent_pid;
- int r;
if (pager_pid > 0)
return 1;
- if ((pager = getenv("SYSTEMD_PAGER")) || (pager = getenv("PAGER")))
- if (!*pager || streq(pager, "cat"))
- return 0;
-
if (!on_tty())
return 0;
+ pager = getenv("SYSTEMD_PAGER");
+ if (!pager)
+ pager = getenv("PAGER");
+
+ /* If the pager is explicitly turned off, honour it */
+ if (pager && (pager[0] == 0 || streq(pager, "cat")))
+ return 0;
+
/* Determine and cache number of columns before we spawn the
* pager so that we get the value from the actual tty */
- columns();
+ (void) columns();
if (pipe(fd) < 0)
return log_error_errno(errno, "Failed to create pager pipe: %m");
@@ -73,11 +83,8 @@ int pager_open(bool jump_to_end) {
parent_pid = getpid();
pager_pid = fork();
- if (pager_pid < 0) {
- r = log_error_errno(errno, "Failed to fork pager: %m");
- safe_close_pair(fd);
- return r;
- }
+ if (pager_pid < 0)
+ return log_error_errno(errno, "Failed to fork pager: %m");
/* In the child start the pager */
if (pager_pid == 0) {
@@ -86,7 +93,7 @@ int pager_open(bool jump_to_end) {
(void) reset_all_signal_handlers();
(void) reset_signal_mask();
- dup2(fd[0], STDIN_FILENO);
+ (void) dup2(fd[0], STDIN_FILENO);
safe_close_pair(fd);
/* Initialize a good set of less options */
@@ -141,7 +148,6 @@ int pager_open(bool jump_to_end) {
if (dup2(fd[1], STDERR_FILENO) < 0)
return log_error_errno(errno, "Failed to duplicate pager pipe: %m");
- safe_close_pair(fd);
return 1;
}
diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c
index d803bbe07e..90114001ee 100644
--- a/src/shared/path-lookup.c
+++ b/src/shared/path-lookup.c
@@ -19,16 +19,20 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
+#include <errno.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
-#include <errno.h>
-#include "util.h"
-#include "strv.h"
-#include "path-util.h"
-#include "path-lookup.h"
+#include "alloc-util.h"
#include "install.h"
+#include "log.h"
+#include "macro.h"
+#include "path-lookup.h"
+#include "path-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "util.h"
int user_config_home(char **config_home) {
const char *e;
@@ -181,7 +185,7 @@ static char** user_dirs(
if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0)
return NULL;
- if (strv_extend_strv(&res, (char**) config_unit_paths) < 0)
+ if (strv_extend_strv(&res, (char**) config_unit_paths, false) < 0)
return NULL;
if (runtime_dir)
@@ -203,14 +207,14 @@ static char** user_dirs(
if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0)
return NULL;
- if (strv_extend_strv(&res, (char**) data_unit_paths) < 0)
+ if (strv_extend_strv(&res, (char**) data_unit_paths, false) < 0)
return NULL;
if (generator_late)
if (strv_extend(&res, generator_late) < 0)
return NULL;
- if (!path_strv_make_absolute_cwd(res))
+ if (path_strv_make_absolute_cwd(res) < 0)
return NULL;
tmp = res;
@@ -244,6 +248,7 @@ int lookup_paths_init(
const char *e;
bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */
+ int r;
assert(p);
@@ -259,9 +264,9 @@ int lookup_paths_init(
/* FIXME: empty components in other places should be
* rejected. */
- p->unit_path = path_split_and_make_absolute(e);
- if (!p->unit_path)
- return -ENOMEM;
+ r = path_split_and_make_absolute(e, &p->unit_path);
+ if (r < 0)
+ return r;
} else
p->unit_path = NULL;
@@ -269,7 +274,6 @@ int lookup_paths_init(
/* Let's figure something out. */
_cleanup_strv_free_ char **unit_path;
- int r;
/* For the user units we include share/ in the search
* path in order to comply with the XDG basedir spec.
@@ -318,7 +322,7 @@ int lookup_paths_init(
if (!unit_path)
return -ENOMEM;
- r = strv_extend_strv(&p->unit_path, unit_path);
+ r = strv_extend_strv(&p->unit_path, unit_path, false);
if (r < 0)
return r;
}
@@ -342,9 +346,9 @@ int lookup_paths_init(
e = getenv("SYSTEMD_SYSVINIT_PATH");
if (e) {
- p->sysvinit_path = path_split_and_make_absolute(e);
- if (!p->sysvinit_path)
- return -ENOMEM;
+ r = path_split_and_make_absolute(e, &p->sysvinit_path);
+ if (r < 0)
+ return r;
} else
p->sysvinit_path = NULL;
@@ -360,9 +364,9 @@ int lookup_paths_init(
e = getenv("SYSTEMD_SYSVRCND_PATH");
if (e) {
- p->sysvrcnd_path = path_split_and_make_absolute(e);
- if (!p->sysvrcnd_path)
- return -ENOMEM;
+ r = path_split_and_make_absolute(e, &p->sysvrcnd_path);
+ if (r < 0)
+ return r;
} else
p->sysvrcnd_path = NULL;
@@ -417,9 +421,8 @@ void lookup_paths_free(LookupPaths *p) {
p->unit_path = strv_free(p->unit_path);
#ifdef HAVE_SYSV_COMPAT
- strv_free(p->sysvinit_path);
- strv_free(p->sysvrcnd_path);
- p->sysvinit_path = p->sysvrcnd_path = NULL;
+ p->sysvinit_path = strv_free(p->sysvinit_path);
+ p->sysvrcnd_path = strv_free(p->sysvrcnd_path);
#endif
}
diff --git a/src/shared/path-lookup.h b/src/shared/path-lookup.h
index e35c8d3c04..b8036718ba 100644
--- a/src/shared/path-lookup.h
+++ b/src/shared/path-lookup.h
@@ -21,6 +21,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <stdbool.h>
#include "macro.h"
typedef struct LookupPaths {
diff --git a/src/shared/ptyfwd.c b/src/shared/ptyfwd.c
index 789f217efc..e6a7a488c9 100644
--- a/src/shared/ptyfwd.c
+++ b/src/shared/ptyfwd.c
@@ -19,19 +19,35 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
-#include <limits.h>
+#include <sys/time.h>
#include <termios.h>
+#include <unistd.h>
-#include "util.h"
+#include "sd-event.h"
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "log.h"
+#include "macro.h"
#include "ptyfwd.h"
+#include "time-util.h"
struct PTYForward {
sd_event *event;
int master;
+ PTYForwardFlags flags;
+
sd_event_source *stdin_event_source;
sd_event_source *stdout_event_source;
sd_event_source *master_event_source;
@@ -41,8 +57,6 @@ struct PTYForward {
struct termios saved_stdin_attr;
struct termios saved_stdout_attr;
- bool read_only:1;
-
bool saved_stdin:1;
bool saved_stdout:1;
@@ -54,8 +68,7 @@ struct PTYForward {
bool master_writable:1;
bool master_hangup:1;
- /* Continue reading after hangup? */
- bool ignore_vhangup:1;
+ bool read_from_master:1;
bool last_char_set:1;
char last_char;
@@ -100,6 +113,18 @@ static bool look_for_escape(PTYForward *f, const char *buffer, size_t n) {
return false;
}
+static bool ignore_vhangup(PTYForward *f) {
+ assert(f);
+
+ if (f->flags & PTY_FORWARD_IGNORE_VHANGUP)
+ return true;
+
+ if ((f->flags & PTY_FORWARD_IGNORE_INITIAL_VHANGUP) && !f->read_from_master)
+ return true;
+
+ return false;
+}
+
static int shovel(PTYForward *f) {
ssize_t k;
@@ -179,7 +204,7 @@ static int shovel(PTYForward *f) {
* EAGAIN here and try again, unless
* ignore_vhangup is off. */
- if (errno == EAGAIN || (errno == EIO && f->ignore_vhangup))
+ if (errno == EAGAIN || (errno == EIO && ignore_vhangup(f)))
f->master_readable = false;
else if (errno == EPIPE || errno == ECONNRESET || errno == EIO) {
f->master_readable = f->master_writable = false;
@@ -190,8 +215,10 @@ static int shovel(PTYForward *f) {
log_error_errno(errno, "read(): %m");
return sd_event_exit(f->event, EXIT_FAILURE);
}
- } else
+ } else {
+ f->read_from_master = true;
f->out_buffer_full += (size_t) k;
+ }
}
if (f->stdout_writable && f->out_buffer_full > 0) {
@@ -302,8 +329,7 @@ static int on_sigwinch_event(sd_event_source *e, const struct signalfd_siginfo *
int pty_forward_new(
sd_event *event,
int master,
- bool ignore_vhangup,
- bool read_only,
+ PTYForwardFlags flags,
PTYForward **ret) {
_cleanup_(pty_forward_freep) PTYForward *f = NULL;
@@ -314,8 +340,7 @@ int pty_forward_new(
if (!f)
return -ENOMEM;
- f->read_only = read_only;
- f->ignore_vhangup = ignore_vhangup;
+ f->flags = flags;
if (event)
f->event = sd_event_ref(event);
@@ -325,7 +350,7 @@ int pty_forward_new(
return r;
}
- if (!read_only) {
+ if (!(flags & PTY_FORWARD_READ_ONLY)) {
r = fd_nonblock(STDIN_FILENO, true);
if (r < 0)
return r;
@@ -344,7 +369,7 @@ int pty_forward_new(
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) >= 0)
(void) ioctl(master, TIOCSWINSZ, &ws);
- if (!read_only) {
+ if (!(flags & PTY_FORWARD_READ_ONLY)) {
if (tcgetattr(STDIN_FILENO, &f->saved_stdin_attr) >= 0) {
struct termios raw_stdin_attr;
@@ -400,6 +425,7 @@ PTYForward *pty_forward_free(PTYForward *f) {
sd_event_source_unref(f->stdin_event_source);
sd_event_source_unref(f->stdout_event_source);
sd_event_source_unref(f->master_event_source);
+ sd_event_source_unref(f->sigwinch_event_source);
sd_event_unref(f->event);
if (f->saved_stdout)
@@ -429,16 +455,20 @@ int pty_forward_get_last_char(PTYForward *f, char *ch) {
return 0;
}
-int pty_forward_set_ignore_vhangup(PTYForward *f, bool ignore_vhangup) {
+int pty_forward_set_ignore_vhangup(PTYForward *f, bool b) {
int r;
assert(f);
- if (f->ignore_vhangup == ignore_vhangup)
+ if (!!(f->flags & PTY_FORWARD_IGNORE_VHANGUP) == b)
return 0;
- f->ignore_vhangup = ignore_vhangup;
- if (!f->ignore_vhangup) {
+ if (b)
+ f->flags |= PTY_FORWARD_IGNORE_VHANGUP;
+ else
+ f->flags &= ~PTY_FORWARD_IGNORE_VHANGUP;
+
+ if (!ignore_vhangup(f)) {
/* We shall now react to vhangup()s? Let's check
* immediately if we might be in one */
@@ -455,5 +485,5 @@ int pty_forward_set_ignore_vhangup(PTYForward *f, bool ignore_vhangup) {
int pty_forward_get_ignore_vhangup(PTYForward *f) {
assert(f);
- return f->ignore_vhangup;
+ return !!(f->flags & PTY_FORWARD_IGNORE_VHANGUP);
}
diff --git a/src/shared/ptyfwd.h b/src/shared/ptyfwd.h
index 6f84e4036a..002590d1cf 100644
--- a/src/shared/ptyfwd.h
+++ b/src/shared/ptyfwd.h
@@ -25,9 +25,21 @@
#include "sd-event.h"
+#include "macro.h"
+
typedef struct PTYForward PTYForward;
-int pty_forward_new(sd_event *event, int master, bool ignore_vhangup, bool read_only, PTYForward **f);
+typedef enum PTYForwardFlags {
+ PTY_FORWARD_READ_ONLY = 1,
+
+ /* Continue reading after hangup? */
+ PTY_FORWARD_IGNORE_VHANGUP = 2,
+
+ /* Continue reading after hangup but only if we never read anything else? */
+ PTY_FORWARD_IGNORE_INITIAL_VHANGUP = 4,
+} PTYForwardFlags;
+
+int pty_forward_new(sd_event *event, int master, PTYForwardFlags flags, PTYForward **f);
PTYForward *pty_forward_free(PTYForward *f);
int pty_forward_get_last_char(PTYForward *f, char *ch);
diff --git a/src/shared/resolve-util.c b/src/shared/resolve-util.c
new file mode 100644
index 0000000000..bf6fc26841
--- /dev/null
+++ b/src/shared/resolve-util.c
@@ -0,0 +1,41 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2016 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 "conf-parser.h"
+#include "resolve-util.h"
+#include "string-table.h"
+
+DEFINE_CONFIG_PARSE_ENUM(config_parse_resolve_support, resolve_support, ResolveSupport, "Failed to parse resolve support setting");
+DEFINE_CONFIG_PARSE_ENUM(config_parse_dnssec_mode, dnssec_mode, DnssecMode, "Failed to parse DNSSEC mode setting");
+
+static const char* const resolve_support_table[_RESOLVE_SUPPORT_MAX] = {
+ [RESOLVE_SUPPORT_NO] = "no",
+ [RESOLVE_SUPPORT_YES] = "yes",
+ [RESOLVE_SUPPORT_RESOLVE] = "resolve",
+};
+DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(resolve_support, ResolveSupport, RESOLVE_SUPPORT_YES);
+
+static const char* const dnssec_mode_table[_DNSSEC_MODE_MAX] = {
+ [DNSSEC_NO] = "no",
+ [DNSSEC_ALLOW_DOWNGRADE] = "allow-downgrade",
+ [DNSSEC_YES] = "yes",
+};
+DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dnssec_mode, DnssecMode, DNSSEC_YES);
diff --git a/src/shared/resolve-util.h b/src/shared/resolve-util.h
new file mode 100644
index 0000000000..fd93a13f73
--- /dev/null
+++ b/src/shared/resolve-util.h
@@ -0,0 +1,62 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2016 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"
+
+typedef enum ResolveSupport ResolveSupport;
+typedef enum DnssecMode DnssecMode;
+
+enum ResolveSupport {
+ RESOLVE_SUPPORT_NO,
+ RESOLVE_SUPPORT_YES,
+ RESOLVE_SUPPORT_RESOLVE,
+ _RESOLVE_SUPPORT_MAX,
+ _RESOLVE_SUPPORT_INVALID = -1
+};
+
+enum DnssecMode {
+ /* No DNSSEC validation is done */
+ DNSSEC_NO,
+
+ /* Validate locally, if the server knows DO, but if not,
+ * don't. Don't trust the AD bit. If the server doesn't do
+ * DNSSEC properly, downgrade to non-DNSSEC operation. Of
+ * course, we then are vulnerable to a downgrade attack, but
+ * that's life and what is configured. */
+ DNSSEC_ALLOW_DOWNGRADE,
+
+ /* Insist on DNSSEC server support, and rather fail than downgrading. */
+ DNSSEC_YES,
+
+ _DNSSEC_MODE_MAX,
+ _DNSSEC_MODE_INVALID = -1
+};
+
+int config_parse_resolve_support(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_dnssec_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* resolve_support_to_string(ResolveSupport p) _const_;
+ResolveSupport resolve_support_from_string(const char *s) _pure_;
+
+const char* dnssec_mode_to_string(DnssecMode p) _const_;
+DnssecMode dnssec_mode_from_string(const char *s) _pure_;
diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c
index d73a74912e..bd1d44a0ab 100644
--- a/src/shared/seccomp-util.c
+++ b/src/shared/seccomp-util.c
@@ -19,10 +19,13 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
#include <seccomp.h>
+#include <stddef.h>
-#include "util.h"
+#include "macro.h"
#include "seccomp-util.h"
+#include "string-util.h"
const char* seccomp_arch_to_string(uint32_t c) {
diff --git a/src/shared/seccomp-util.h b/src/shared/seccomp-util.h
index 60d97154ec..79ee8c728d 100644
--- a/src/shared/seccomp-util.h
+++ b/src/shared/seccomp-util.h
@@ -22,6 +22,7 @@
***/
#include <seccomp.h>
+#include <stdint.h>
const char* seccomp_arch_to_string(uint32_t c);
int seccomp_arch_from_string(const char *n, uint32_t *ret);
diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c
index 1064fd5cbd..7ba11e2f0e 100644
--- a/src/shared/sleep-config.c
+++ b/src/shared/sleep-config.c
@@ -19,14 +19,25 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include "alloc-util.h"
#include "conf-parser.h"
-#include "sleep-config.h"
+#include "def.h"
+#include "fd-util.h"
#include "fileio.h"
#include "log.h"
+#include "macro.h"
+#include "parse-util.h"
+#include "sleep-config.h"
+#include "string-util.h"
#include "strv.h"
-#include "util.h"
#define USE(x, y) do{ (x) = (y); (y) = NULL; } while(0)
@@ -49,7 +60,7 @@ int parse_sleep_config(const char *verb, char ***_modes, char ***_states) {
};
config_parse_many(PKGSYSCONFDIR "/sleep.conf",
- CONF_DIRS_NULSTR("systemd/sleep.conf"),
+ CONF_PATHS_NULSTR("systemd/sleep.conf.d"),
"Sleep\0", config_item_table_lookup, items,
false, NULL);
@@ -226,7 +237,7 @@ static bool enough_memory_for_hibernation(void) {
if (r < 0)
return false;
- r = get_status_field("/proc/meminfo", "\nActive(anon):", &active);
+ r = get_proc_field("/proc/meminfo", "Active(anon)", WHITESPACE, &active);
if (r < 0) {
log_error_errno(r, "Failed to retrieve Active(anon) from /proc/meminfo: %m");
return false;
diff --git a/src/shared/spawn-ask-password-agent.c b/src/shared/spawn-ask-password-agent.c
index 70466d17e5..3fcea61873 100644
--- a/src/shared/spawn-ask-password-agent.c
+++ b/src/shared/spawn-ask-password-agent.c
@@ -19,14 +19,14 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
-#include <signal.h>
#include "log.h"
-#include "util.h"
#include "process-util.h"
#include "spawn-ask-password-agent.h"
+#include "util.h"
static pid_t agent_pid = 0;
@@ -46,9 +46,9 @@ int ask_password_agent_open(void) {
SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH,
SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, "--watch", NULL);
if (r < 0)
- log_error_errno(r, "Failed to fork TTY ask password agent: %m");
+ return log_error_errno(r, "Failed to fork TTY ask password agent: %m");
- return r;
+ return 1;
}
void ask_password_agent_close(void) {
@@ -57,8 +57,8 @@ void ask_password_agent_close(void) {
return;
/* Inform agent that we are done */
- kill(agent_pid, SIGTERM);
- kill(agent_pid, SIGCONT);
+ (void) kill(agent_pid, SIGTERM);
+ (void) kill(agent_pid, SIGCONT);
(void) wait_for_terminate(agent_pid, NULL);
agent_pid = 0;
}
diff --git a/src/shared/spawn-polkit-agent.c b/src/shared/spawn-polkit-agent.c
index 4db249e1ca..ada4bdb17e 100644
--- a/src/shared/spawn-polkit-agent.c
+++ b/src/shared/spawn-polkit-agent.c
@@ -19,16 +19,21 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
-#include <unistd.h>
-#include <signal.h>
#include <errno.h>
#include <poll.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "fd-util.h"
+#include "io-util.h"
#include "log.h"
-#include "util.h"
+#include "macro.h"
#include "process-util.h"
#include "spawn-polkit-agent.h"
+#include "stdio-util.h"
+#include "time-util.h"
+#include "util.h"
#ifdef ENABLE_POLKIT
static pid_t agent_pid = 0;
@@ -76,8 +81,9 @@ void polkit_agent_close(void) {
return;
/* Inform agent that we are done */
- kill(agent_pid, SIGTERM);
- kill(agent_pid, SIGCONT);
+ (void) kill(agent_pid, SIGTERM);
+ (void) kill(agent_pid, SIGCONT);
+
(void) wait_for_terminate(agent_pid, NULL);
agent_pid = 0;
}
diff --git a/src/shared/specifier.c b/src/shared/specifier.c
index 85bd477f2d..841f4654b0 100644
--- a/src/shared/specifier.c
+++ b/src/shared/specifier.c
@@ -19,13 +19,20 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
#include <string.h>
#include <sys/utsname.h>
-#include "macro.h"
-#include "util.h"
+#include "sd-id128.h"
+
+#include "alloc-util.h"
#include "hostname-util.h"
+#include "macro.h"
#include "specifier.h"
+#include "string-util.h"
/*
* Generic infrastructure for replacing %x style specifiers in
diff --git a/src/shared/switch-root.c b/src/shared/switch-root.c
index b12189cd10..bf0739e5fa 100644
--- a/src/shared/switch-root.c
+++ b/src/shared/switch-root.c
@@ -19,21 +19,27 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/stat.h>
-#include <stdbool.h>
#include <errno.h>
-#include <string.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
#include <sys/mount.h>
+#include <sys/stat.h>
#include <unistd.h>
-#include <fcntl.h>
-#include "util.h"
-#include "path-util.h"
-#include "mkdir.h"
-#include "rm-rf.h"
#include "base-filesystem.h"
+#include "fd-util.h"
+#include "log.h"
#include "missing.h"
+#include "mkdir.h"
+#include "path-util.h"
+#include "rm-rf.h"
+#include "stdio-util.h"
+#include "string-util.h"
#include "switch-root.h"
+#include "user-util.h"
+#include "util.h"
int switch_root(const char *new_root, const char *oldroot, bool detach_oldroot, unsigned long mountflags) {
@@ -72,7 +78,7 @@ int switch_root(const char *new_root, const char *oldroot, bool detach_oldroot,
char new_mount[PATH_MAX];
struct stat sb;
- snprintf(new_mount, sizeof(new_mount), "%s%s", new_root, i);
+ xsprintf(new_mount, "%s%s", new_root, i);
mkdir_p_label(new_mount, 0755);
diff --git a/src/shared/switch-root.h b/src/shared/switch-root.h
index adf893a922..1350fd9b1c 100644
--- a/src/shared/switch-root.h
+++ b/src/shared/switch-root.h
@@ -2,6 +2,7 @@
#pragma once
+#include <stdbool.h>
/***
This file is part of systemd.
diff --git a/src/shared/sysctl-util.c b/src/shared/sysctl-util.c
index 1de0b94fd5..a2cb6e9763 100644
--- a/src/shared/sysctl-util.c
+++ b/src/shared/sysctl-util.c
@@ -19,18 +19,13 @@
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 <string.h>
-#include "log.h"
-#include "util.h"
#include "fileio.h"
-#include "build.h"
+#include "log.h"
+#include "macro.h"
+#include "string-util.h"
#include "sysctl-util.h"
char *sysctl_normalize(char *s) {
diff --git a/src/shared/uid-range.c b/src/shared/uid-range.c
index 4794ff45bb..1ecef5a44c 100644
--- a/src/shared/uid-range.c
+++ b/src/shared/uid-range.c
@@ -19,8 +19,13 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "macro.h"
#include "uid-range.h"
+#include "user-util.h"
static bool uid_range_intersect(UidRange *range, uid_t start, uid_t nr) {
assert(range);
diff --git a/src/shared/utmp-wtmp.c b/src/shared/utmp-wtmp.c
index 63f1e4ca6f..e72f6fa1a2 100644
--- a/src/shared/utmp-wtmp.c
+++ b/src/shared/utmp-wtmp.c
@@ -19,18 +19,28 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <utmpx.h>
#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
+#include <sys/time.h>
#include <sys/utsname.h>
-#include <fcntl.h>
#include <unistd.h>
-#include <poll.h>
+#include <utmpx.h>
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "hostname-util.h"
#include "macro.h"
#include "path-util.h"
+#include "string-util.h"
#include "terminal-util.h"
-#include "hostname-util.h"
+#include "time-util.h"
+#include "user-util.h"
+#include "util.h"
#include "utmp-wtmp.h"
int utmp_get_runlevel(int *runlevel, int *previous) {
diff --git a/src/shared/utmp-wtmp.h b/src/shared/utmp-wtmp.h
index e0ceb873ac..3aec3f959d 100644
--- a/src/shared/utmp-wtmp.h
+++ b/src/shared/utmp-wtmp.h
@@ -21,6 +21,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <stdbool.h>
+#include <sys/types.h>
+
+#include "time-util.h"
#include "util.h"
#ifdef HAVE_UTMP
diff --git a/src/shared/watchdog.c b/src/shared/watchdog.c
index 9d39beb340..bc171817ea 100644
--- a/src/shared/watchdog.c
+++ b/src/shared/watchdog.c
@@ -19,14 +19,17 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/ioctl.h>
#include <errno.h>
#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <syslog.h>
#include <unistd.h>
#include <linux/watchdog.h>
-#include "watchdog.h"
+#include "fd-util.h"
#include "log.h"
+#include "time-util.h"
+#include "watchdog.h"
static int watchdog_fd = -1;
static usec_t watchdog_timeout = USEC_INFINITY;
diff --git a/src/shared/watchdog.h b/src/shared/watchdog.h
index b748b15857..fd1c11a644 100644
--- a/src/shared/watchdog.h
+++ b/src/shared/watchdog.h
@@ -21,6 +21,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <stdbool.h>
+
+#include "time-util.h"
#include "util.h"
int watchdog_set_timeout(usec_t *usec);
diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c
index 2b2310152d..95de369817 100644
--- a/src/sleep/sleep.c
+++ b/src/sleep/sleep.c
@@ -20,18 +20,20 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
#include <errno.h>
#include <getopt.h>
+#include <stdio.h>
#include "sd-messages.h"
-#include "log.h"
-#include "util.h"
-#include "strv.h"
+
+#include "def.h"
+#include "fd-util.h"
#include "fileio.h"
-#include "build.h"
+#include "log.h"
#include "sleep-config.h"
-#include "def.h"
+#include "string-util.h"
+#include "strv.h"
+#include "util.h"
static char* arg_verb = NULL;
@@ -165,9 +167,7 @@ static int parse_argv(int argc, char *argv[]) {
return 0; /* done */
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0 /* done */;
+ return version();
case '?':
return -EINVAL;
diff --git a/src/socket-proxy/socket-proxyd.c b/src/socket-proxy/socket-proxyd.c
index 715f440cb1..ba82adadb4 100644
--- a/src/socket-proxy/socket-proxyd.c
+++ b/src/socket-proxy/socket-proxyd.c
@@ -20,12 +20,12 @@
***/
#include <errno.h>
+#include <fcntl.h>
#include <getopt.h>
+#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <netdb.h>
-#include <fcntl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
@@ -33,12 +33,15 @@
#include "sd-daemon.h"
#include "sd-event.h"
#include "sd-resolve.h"
+
+#include "alloc-util.h"
+#include "fd-util.h"
#include "log.h"
+#include "path-util.h"
+#include "set.h"
#include "socket-util.h"
+#include "string-util.h"
#include "util.h"
-#include "build.h"
-#include "set.h"
-#include "path-util.h"
#define BUFFER_SIZE (256 * 1024)
#define CONNECTIONS_MAX 256
@@ -603,9 +606,7 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case '?':
return -EINVAL;
diff --git a/src/sysctl/sysctl.c b/src/sysctl/sysctl.c
index 618844382f..25b5ff52ea 100644
--- a/src/sysctl/sysctl.c
+++ b/src/sysctl/sysctl.c
@@ -19,27 +19,29 @@
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 <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
-#include "log.h"
-#include "strv.h"
-#include "util.h"
-#include "hashmap.h"
-#include "path-util.h"
#include "conf-files.h"
+#include "def.h"
+#include "fd-util.h"
#include "fileio.h"
-#include "build.h"
+#include "hashmap.h"
+#include "log.h"
+#include "path-util.h"
+#include "string-util.h"
+#include "strv.h"
#include "sysctl-util.h"
+#include "util.h"
static char **arg_prefixes = NULL;
-static const char conf_file_dirs[] = CONF_DIRS_NULSTR("sysctl");
+static const char conf_file_dirs[] = CONF_PATHS_NULSTR("sysctl.d");
static int apply_all(Hashmap *sysctl_options) {
char *property, *value;
@@ -86,8 +88,7 @@ static int parse_file(Hashmap *sysctl_options, const char *path, bool ignore_eno
if (feof(f))
break;
- log_error_errno(errno, "Failed to read file '%s', ignoring: %m", path);
- return -errno;
+ return log_error_errno(errno, "Failed to read file '%s', ignoring: %m", path);
}
p = strstrip(l);
@@ -195,9 +196,7 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case ARG_PREFIX: {
char *p;
diff --git a/src/system-update-generator/system-update-generator.c b/src/system-update-generator/system-update-generator.c
index 00045150f6..6c2f53774d 100644
--- a/src/system-update-generator/system-update-generator.c
+++ b/src/system-update-generator/system-update-generator.c
@@ -22,7 +22,9 @@
#include <errno.h>
#include <unistd.h>
+#include "fs-util.h"
#include "log.h"
+#include "string-util.h"
#include "util.h"
/*
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 34e4751b94..d021d30f78 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -37,7 +37,7 @@
#include "sd-daemon.h"
#include "sd-login.h"
-#include "build.h"
+#include "alloc-util.h"
#include "bus-common-errors.h"
#include "bus-error.h"
#include "bus-message.h"
@@ -49,31 +49,42 @@
#include "efivars.h"
#include "env-util.h"
#include "exit-status.h"
+#include "fd-util.h"
#include "fileio.h"
#include "formats-util.h"
+#include "fs-util.h"
+#include "glob-util.h"
#include "hostname-util.h"
#include "initreq.h"
#include "install.h"
+#include "io-util.h"
#include "list.h"
+#include "locale-util.h"
#include "log.h"
#include "logs-show.h"
#include "macro.h"
#include "mkdir.h"
#include "pager.h"
+#include "parse-util.h"
#include "path-lookup.h"
#include "path-util.h"
#include "process-util.h"
+#include "rlimit-util.h"
#include "set.h"
#include "signal-util.h"
#include "socket-util.h"
#include "spawn-ask-password-agent.h"
#include "spawn-polkit-agent.h"
#include "special.h"
+#include "stat-util.h"
#include "strv.h"
#include "terminal-util.h"
#include "unit-name.h"
+#include "user-util.h"
#include "util.h"
#include "utmp-wtmp.h"
+#include "verbs.h"
+#include "virt.h"
static char **arg_types = NULL;
static char **arg_states = NULL;
@@ -107,7 +118,7 @@ static UnitFilePresetMode arg_preset_mode = UNIT_FILE_PRESET_FULL;
static char **arg_wall = NULL;
static const char *arg_kill_who = NULL;
static int arg_signal = SIGTERM;
-static const char *arg_root = NULL;
+static char *arg_root = NULL;
static usec_t arg_when = 0;
static enum action {
_ACTION_INVALID,
@@ -134,23 +145,61 @@ static enum action {
_ACTION_MAX
} arg_action = ACTION_SYSTEMCTL;
static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
-static char *arg_host = NULL;
+static const 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;
-
-static int daemon_reload(sd_bus *bus, char **args);
+static int daemon_reload(int argc, char *argv[], void* userdata);
static int halt_now(enum action a);
static int check_one_unit(sd_bus *bus, const char *name, const char *good_states, bool quiet);
-static char** strv_skip_first(char **strv) {
- if (strv_length(strv) > 0)
- return strv + 1;
- return NULL;
+static bool original_stdout_is_tty;
+
+typedef enum BusFocus {
+ BUS_FULL, /* The full bus indicated via --system or --user */
+ BUS_MANAGER, /* The manager itself, possibly directly, possibly via the bus */
+ _BUS_FOCUS_MAX
+} BusFocus;
+
+static sd_bus *busses[_BUS_FOCUS_MAX] = {};
+
+static int acquire_bus(BusFocus focus, sd_bus **ret) {
+ int r;
+
+ assert(focus < _BUS_FOCUS_MAX);
+ assert(ret);
+
+ /* We only go directly to the manager, if we are using a local transport */
+ if (arg_transport != BUS_TRANSPORT_LOCAL)
+ focus = BUS_FULL;
+
+ if (!busses[focus]) {
+ bool user;
+
+ user = arg_scope != UNIT_FILE_SYSTEM;
+
+ if (focus == BUS_MANAGER)
+ r = bus_connect_transport_systemd(arg_transport, arg_host, user, &busses[focus]);
+ else
+ r = bus_connect_transport(arg_transport, arg_host, user, &busses[focus]);
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to bus: %m");
+
+ (void) sd_bus_set_allow_interactive_authorization(busses[focus], arg_ask_password);
+ }
+
+ *ret = busses[focus];
+ return 0;
+}
+
+static void release_busses(void) {
+ BusFocus w;
+
+ for (w = 0; w < _BUS_FOCUS_MAX; w++)
+ busses[w] = sd_bus_flush_close_unref(busses[w]);
}
static void pager_open_if_enabled(void) {
@@ -198,7 +247,7 @@ static OutputFlags get_output_flags(void) {
arg_all * OUTPUT_SHOW_ALL |
arg_full * OUTPUT_FULL_WIDTH |
(!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
- on_tty() * OUTPUT_COLOR |
+ colors_enabled() * OUTPUT_COLOR |
!arg_quiet * OUTPUT_WARN_CUTOFF;
}
@@ -230,42 +279,10 @@ static int translate_bus_error_to_exit_status(int r, const sd_bus_error *error)
return EXIT_FAILURE;
}
-static void warn_wall(enum action a) {
- static const char *table[_ACTION_MAX] = {
- [ACTION_HALT] = "The system is going down for system halt NOW!",
- [ACTION_REBOOT] = "The system is going down for reboot NOW!",
- [ACTION_POWEROFF] = "The system is going down for power-off NOW!",
- [ACTION_KEXEC] = "The system is going down for kexec reboot NOW!",
- [ACTION_RESCUE] = "The system is going down to rescue mode NOW!",
- [ACTION_EMERGENCY] = "The system is going down to emergency mode NOW!",
- [ACTION_CANCEL_SHUTDOWN] = "The system shutdown has been cancelled NOW!"
- };
+static bool install_client_side(void) {
- if (arg_no_wall)
- return;
-
- if (arg_wall) {
- _cleanup_free_ char *p;
-
- p = strv_join(arg_wall, " ");
- if (!p) {
- log_oom();
- return;
- }
-
- if (*p) {
- utmp_wall(p, NULL, NULL, NULL, NULL);
- return;
- }
- }
-
- if (!table[a])
- return;
-
- utmp_wall(table[a], NULL, NULL, NULL, NULL);
-}
-
-static bool avoid_bus(void) {
+ /* Decides when to execute enable/disable/... operations
+ * client-side rather than server-side. */
if (running_in_chroot() > 0)
return true;
@@ -279,6 +296,10 @@ static bool avoid_bus(void) {
if (arg_scope == UNIT_FILE_GLOBAL)
return true;
+ /* Unsupported environment variable, mostly for debugging purposes */
+ if (getenv_bool("SYSTEMCTL_INSTALL_CLIENT_SIDE") > 0)
+ return true;
+
return false;
}
@@ -509,9 +530,9 @@ static int get_unit_list(
int c,
sd_bus_message **_reply) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
size_t size = c;
int r;
UnitInfo u;
@@ -616,7 +637,7 @@ static int get_unit_list_recursive(
return log_error_errno(r, "Failed to get machine names: %m");
STRV_FOREACH(i, machines) {
- _cleanup_bus_flush_close_unref_ sd_bus *container = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *container = NULL;
int k;
r = sd_bus_open_system_machine(&container, *i);
@@ -652,15 +673,20 @@ static int get_unit_list_recursive(
return c;
}
-static int list_units(sd_bus *bus, char **args) {
+static int list_units(int argc, char *argv[], void *userdata) {
_cleanup_free_ UnitInfo *unit_infos = NULL;
_cleanup_(message_set_freep) Set *replies = NULL;
_cleanup_strv_free_ char **machines = NULL;
+ sd_bus *bus;
int r;
pager_open_if_enabled();
- r = get_unit_list_recursive(bus, strv_skip_first(args), &unit_infos, &replies, &machines);
+ r = acquire_bus(BUS_MANAGER, &bus);
+ if (r < 0)
+ return r;
+
+ r = get_unit_list_recursive(bus, strv_skip(argv, 1), &unit_infos, &replies, &machines);
if (r < 0)
return r;
@@ -673,9 +699,13 @@ static int get_triggered_units(
const char* path,
char*** ret) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
+ assert(bus);
+ assert(path);
+ assert(ret);
+
r = sd_bus_get_property_strv(
bus,
"org.freedesktop.systemd1",
@@ -684,9 +714,8 @@ static int get_triggered_units(
"Triggers",
&error,
ret);
-
if (r < 0)
- log_error("Failed to determine triggers: %s", bus_error_message(&error, r));
+ return log_error_errno(r, "Failed to determine triggers: %s", bus_error_message(&error, r));
return 0;
}
@@ -696,8 +725,8 @@ static int get_listening(
const char* unit_path,
char*** listening) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
const char *type, *path;
int r, n = 0;
@@ -710,10 +739,8 @@ static int get_listening(
&error,
&reply,
"a(ss)");
- if (r < 0) {
- log_error("Failed to get list of listening sockets: %s", bus_error_message(&error, r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get list of listening sockets: %s", bus_error_message(&error, r));
r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ss)");
if (r < 0)
@@ -853,7 +880,7 @@ static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) {
return 0;
}
-static int list_sockets(sd_bus *bus, char **args) {
+static int list_sockets(int argc, char *argv[], void *userdata) {
_cleanup_(message_set_freep) Set *replies = NULL;
_cleanup_strv_free_ char **machines = NULL;
_cleanup_free_ UnitInfo *unit_infos = NULL;
@@ -863,10 +890,15 @@ static int list_sockets(sd_bus *bus, char **args) {
unsigned cs = 0;
size_t size = 0;
int r = 0, n;
+ sd_bus *bus;
pager_open_if_enabled();
- n = get_unit_list_recursive(bus, strv_skip_first(args), &unit_infos, &replies, &machines);
+ r = acquire_bus(BUS_MANAGER, &bus);
+ if (r < 0)
+ return r;
+
+ n = get_unit_list_recursive(bus, strv_skip(argv, 1), &unit_infos, &replies, &machines);
if (n < 0)
return n;
@@ -930,7 +962,7 @@ static int get_next_elapse(
const char *path,
dual_timestamp *next) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
dual_timestamp t;
int r;
@@ -947,10 +979,8 @@ static int get_next_elapse(
&error,
't',
&t.monotonic);
- if (r < 0) {
- log_error("Failed to get next elapsation time: %s", bus_error_message(&error, r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get next elapsation time: %s", bus_error_message(&error, r));
r = sd_bus_get_property_trivial(
bus,
@@ -961,10 +991,8 @@ static int get_next_elapse(
&error,
't',
&t.realtime);
- if (r < 0) {
- log_error("Failed to get next elapsation time: %s", bus_error_message(&error, r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get next elapsation time: %s", bus_error_message(&error, r));
*next = t;
return 0;
@@ -975,7 +1003,7 @@ static int get_last_trigger(
const char *path,
usec_t *last) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert(bus);
@@ -991,10 +1019,8 @@ static int get_last_trigger(
&error,
't',
last);
- if (r < 0) {
- log_error("Failed to get last trigger time: %s", bus_error_message(&error, r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get last trigger time: %s", bus_error_message(&error, r));
return 0;
}
@@ -1160,7 +1186,7 @@ static usec_t calc_next_elapse(dual_timestamp *nw, dual_timestamp *next) {
return next_elapse;
}
-static int list_timers(sd_bus *bus, char **args) {
+static int list_timers(int argc, char *argv[], void *userdata) {
_cleanup_(message_set_freep) Set *replies = NULL;
_cleanup_strv_free_ char **machines = NULL;
_cleanup_free_ struct timer_info *timer_infos = NULL;
@@ -1170,11 +1196,16 @@ static int list_timers(sd_bus *bus, char **args) {
size_t size = 0;
int n, c = 0;
dual_timestamp nw;
+ sd_bus *bus;
int r = 0;
pager_open_if_enabled();
- n = get_unit_list_recursive(bus, strv_skip_first(args), &unit_infos, &replies, &machines);
+ r = acquire_bus(BUS_MANAGER, &bus);
+ if (r < 0)
+ return r;
+
+ n = get_unit_list_recursive(bus, strv_skip(argv, 1), &unit_infos, &replies, &machines);
if (n < 0)
return n;
@@ -1304,7 +1335,7 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) {
UNIT_FILE_MASKED,
UNIT_FILE_MASKED_RUNTIME,
UNIT_FILE_DISABLED,
- UNIT_FILE_INVALID)) {
+ UNIT_FILE_BAD)) {
on = ansi_highlight_red();
off = ansi_normal();
} else if (u->state == UNIT_FILE_ENABLED) {
@@ -1326,8 +1357,8 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) {
printf("\n%u unit files listed.\n", c);
}
-static int list_unit_files(sd_bus *bus, char **args) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+static int list_unit_files(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_free_ UnitFileList *units = NULL;
UnitFileList *unit;
size_t size = 0;
@@ -1338,7 +1369,7 @@ static int list_unit_files(sd_bus *bus, char **args) {
pager_open_if_enabled();
- if (avoid_bus()) {
+ if (install_client_side()) {
Hashmap *h;
UnitFileList *u;
Iterator i;
@@ -1351,8 +1382,7 @@ static int list_unit_files(sd_bus *bus, char **args) {
r = unit_file_get_list(arg_scope, arg_root, h);
if (r < 0) {
unit_file_list_free(h);
- log_error_errno(r, "Failed to get unit file list: %m");
- return r;
+ return log_error_errno(r, "Failed to get unit file list: %m");
}
n_units = hashmap_size(h);
@@ -1364,7 +1394,7 @@ static int list_unit_files(sd_bus *bus, char **args) {
}
HASHMAP_FOREACH(u, h, i) {
- if (!output_show_unit_file(u, strv_skip_first(args)))
+ if (!output_show_unit_file(u, strv_skip(argv, 1)))
continue;
units[c++] = *u;
@@ -1374,7 +1404,12 @@ 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;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ sd_bus *bus;
+
+ r = acquire_bus(BUS_MANAGER, &bus);
+ if (r < 0)
+ return r;
r = sd_bus_call_method(
bus,
@@ -1385,10 +1420,8 @@ static int list_unit_files(sd_bus *bus, char **args) {
&error,
&reply,
NULL);
- if (r < 0) {
- log_error("Failed to list unit files: %s", bus_error_message(&error, r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to list unit files: %s", bus_error_message(&error, r));
r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ss)");
if (r < 0)
@@ -1404,7 +1437,7 @@ static int list_unit_files(sd_bus *bus, char **args) {
unit_file_state_from_string(state)
};
- if (output_show_unit_file(&units[c], strv_skip_first(args)))
+ if (output_show_unit_file(&units[c], strv_skip(argv, 1)))
c ++;
}
@@ -1419,7 +1452,7 @@ static int list_unit_files(sd_bus *bus, char **args) {
qsort_safe(units, c, sizeof(UnitFileList), compare_unit_file_list);
output_unit_file_list(units, c);
- if (avoid_bus()) {
+ if (install_client_side()) {
for (unit = units; unit < units + c; unit++)
free(unit->path);
}
@@ -1470,16 +1503,12 @@ static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, cha
static const char *dependencies[_DEPENDENCY_MAX] = {
[DEPENDENCY_FORWARD] = "Requires\0"
- "RequiresOverridable\0"
"Requisite\0"
- "RequisiteOverridable\0"
"Wants\0"
"ConsistsOf\0"
"BindsTo\0",
[DEPENDENCY_REVERSE] = "RequiredBy\0"
- "RequiredByOverridable\0"
"RequisiteOf\0"
- "RequisiteOfOverridable\0"
"WantedBy\0"
"PartOf\0"
"BoundBy\0",
@@ -1487,8 +1516,8 @@ static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, cha
[DEPENDENCY_BEFORE] = "Before\0",
};
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_strv_free_ char **ret = NULL;
_cleanup_free_ char *path = NULL;
int r;
@@ -1511,10 +1540,8 @@ static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, cha
&error,
&reply,
"s", "org.freedesktop.systemd1.Unit");
- if (r < 0) {
- log_error("Failed to get properties of %s: %s", name, bus_error_message(&error, r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get properties of %s: %s", name, bus_error_message(&error, r));
r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sv}");
if (r < 0)
@@ -1638,16 +1665,15 @@ static int list_dependencies_one(
return 0;
}
-static int list_dependencies(sd_bus *bus, char **args) {
+static int list_dependencies(int argc, char *argv[], void *userdata) {
_cleanup_strv_free_ char **units = NULL;
_cleanup_free_ char *unit = NULL;
const char *u;
+ sd_bus *bus;
int r;
- assert(bus);
-
- if (args[1]) {
- r = unit_name_mangle(args[1], UNIT_NAME_NOGLOB, &unit);
+ if (argv[1]) {
+ r = unit_name_mangle(argv[1], UNIT_NAME_NOGLOB, &unit);
if (r < 0)
return log_error_errno(r, "Failed to mangle unit name: %m");
@@ -1657,6 +1683,10 @@ static int list_dependencies(sd_bus *bus, char **args) {
pager_open_if_enabled();
+ r = acquire_bus(BUS_MANAGER, &bus);
+ if (r < 0)
+ return r;
+
puts(u);
return list_dependencies_one(bus, u, 0, &units, 0);
@@ -1712,7 +1742,7 @@ static int compare_machine_info(const void *a, const void *b) {
}
static int get_machine_properties(sd_bus *bus, struct machine_info *mi) {
- _cleanup_bus_flush_close_unref_ sd_bus *container = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *container = NULL;
int r;
assert(mi);
@@ -1872,12 +1902,11 @@ static void output_machines_list(struct machine_info *machine_infos, unsigned n)
printf("\n%u machines listed.\n", n);
}
-static int list_machines(sd_bus *bus, char **args) {
+static int list_machines(int argc, char *argv[], void *userdata) {
struct machine_info *machine_infos = NULL;
+ sd_bus *bus;
int r;
- assert(bus);
-
if (geteuid() != 0) {
log_error("Must be root.");
return -EPERM;
@@ -1885,7 +1914,11 @@ static int list_machines(sd_bus *bus, char **args) {
pager_open_if_enabled();
- r = get_machine_list(bus, &machine_infos, strv_skip_first(args));
+ r = acquire_bus(BUS_MANAGER, &bus);
+ if (r < 0)
+ return r;
+
+ r = get_machine_list(bus, &machine_infos, strv_skip(argv, 1));
if (r < 0)
return r;
@@ -1896,20 +1929,25 @@ static int list_machines(sd_bus *bus, char **args) {
return 0;
}
-static int get_default(sd_bus *bus, char **args) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+static int get_default(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_free_ char *_path = NULL;
const char *path;
int r;
- if (!bus || avoid_bus()) {
+ if (install_client_side()) {
r = unit_file_get_default(arg_scope, arg_root, &_path);
if (r < 0)
return log_error_errno(r, "Failed to get default target: %m");
path = _path;
} else {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ sd_bus *bus;
+
+ r = acquire_bus(BUS_MANAGER, &bus);
+ if (r < 0)
+ return r;
r = sd_bus_call_method(
bus,
@@ -1920,10 +1958,8 @@ static int get_default(sd_bus *bus, char **args) {
&error,
&reply,
NULL);
- if (r < 0) {
- log_error("Failed to get default target: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get default target: %s", bus_error_message(&error, r));
r = sd_bus_message_read(reply, "s", &path);
if (r < 0)
@@ -1943,23 +1979,27 @@ static void dump_unit_file_changes(const UnitFileChange *changes, unsigned n_cha
for (i = 0; i < n_changes; i++) {
if (changes[i].type == UNIT_FILE_SYMLINK)
- log_info("Created symlink from %s to %s.", changes[i].path, changes[i].source);
+ log_info("Created symlink %s, pointing to %s.", changes[i].path, changes[i].source);
else
log_info("Removed symlink %s.", changes[i].path);
}
}
-static int set_default(sd_bus *bus, char **args) {
+static int set_default(int argc, char *argv[], void *userdata) {
_cleanup_free_ char *unit = NULL;
- UnitFileChange *changes = NULL;
- unsigned n_changes = 0;
int r;
- r = unit_name_mangle_with_suffix(args[1], UNIT_NAME_NOGLOB, ".target", &unit);
+ assert(argc >= 2);
+ assert(argv);
+
+ r = unit_name_mangle_with_suffix(argv[1], UNIT_NAME_NOGLOB, ".target", &unit);
if (r < 0)
return log_error_errno(r, "Failed to mangle unit name: %m");
- if (!bus || avoid_bus()) {
+ if (install_client_side()) {
+ UnitFileChange *changes = NULL;
+ unsigned n_changes = 0;
+
r = unit_file_set_default(arg_scope, arg_root, unit, true, &changes, &n_changes);
if (r < 0)
return log_error_errno(r, "Failed to set default target: %m");
@@ -1967,13 +2007,19 @@ static int set_default(sd_bus *bus, char **args) {
if (!arg_quiet)
dump_unit_file_changes(changes, n_changes);
+ unit_file_changes_free(changes, n_changes);
r = 0;
} else {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ sd_bus *bus;
polkit_agent_open_if_enabled();
+ r = acquire_bus(BUS_MANAGER, &bus);
+ if (r < 0)
+ return r;
+
r = sd_bus_call_method(
bus,
"org.freedesktop.systemd1",
@@ -1983,10 +2029,8 @@ static int set_default(sd_bus *bus, char **args) {
&error,
&reply,
"sb", unit, 1);
- if (r < 0) {
- log_error("Failed to set default target: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to set default target: %s", bus_error_message(&error, r));
r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, NULL, NULL);
if (r < 0)
@@ -1994,13 +2038,11 @@ static int set_default(sd_bus *bus, char **args) {
/* Try to reload if enabled */
if (!arg_no_reload)
- r = daemon_reload(bus, args);
+ r = daemon_reload(argc, argv, userdata);
else
r = 0;
}
- unit_file_changes_free(changes, n_changes);
-
return r;
}
@@ -2085,17 +2127,24 @@ static bool output_show_job(struct job_info *job, char **patterns) {
return strv_fnmatch_or_empty(patterns, job->name, FNM_NOESCAPE);
}
-static int list_jobs(sd_bus *bus, char **args) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+static int list_jobs(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
const char *name, *type, *state, *job_path, *unit_path;
_cleanup_free_ struct job_info *jobs = NULL;
size_t size = 0;
unsigned c = 0;
+ sd_bus *bus;
uint32_t id;
int r;
bool skipped = false;
+ pager_open_if_enabled();
+
+ r = acquire_bus(BUS_MANAGER, &bus);
+ if (r < 0)
+ return r;
+
r = sd_bus_call_method(
bus,
"org.freedesktop.systemd1",
@@ -2105,10 +2154,8 @@ static int list_jobs(sd_bus *bus, char **args) {
&error,
&reply,
NULL);
- if (r < 0) {
- log_error("Failed to list jobs: %s", bus_error_message(&error, r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to list jobs: %s", bus_error_message(&error, r));
r = sd_bus_message_enter_container(reply, 'a', "(usssoo)");
if (r < 0)
@@ -2117,7 +2164,7 @@ static int list_jobs(sd_bus *bus, char **args) {
while ((r = sd_bus_message_read(reply, "(usssoo)", &id, &name, &type, &state, &job_path, &unit_path)) > 0) {
struct job_info job = { id, name, type, state };
- if (!output_show_job(&job, strv_skip_first(args))) {
+ if (!output_show_job(&job, strv_skip(argv, 1))) {
skipped = true;
continue;
}
@@ -2135,22 +2182,25 @@ static int list_jobs(sd_bus *bus, char **args) {
return bus_log_parse_error(r);
output_jobs_list(jobs, c, skipped);
- return r;
+ return 0;
}
-static int cancel_job(sd_bus *bus, char **args) {
+static int cancel_job(int argc, char *argv[], void *userdata) {
+ sd_bus *bus;
char **name;
int r = 0;
- assert(args);
-
- if (strv_length(args) <= 1)
- return daemon_reload(bus, args);
+ if (argc <= 1)
+ return daemon_reload(argc, argv, userdata);
polkit_agent_open_if_enabled();
- STRV_FOREACH(name, args+1) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ r = acquire_bus(BUS_MANAGER, &bus);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(name, strv_skip(argv, 1)) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
uint32_t id;
int q;
@@ -2168,7 +2218,7 @@ static int cancel_job(sd_bus *bus, char **args) {
NULL,
"u", id);
if (q < 0) {
- log_error("Failed to cancel job %"PRIu32": %s", id, bus_error_message(&error, q));
+ log_error_errno(q, "Failed to cancel job %"PRIu32": %s", id, bus_error_message(&error, q));
if (r == 0)
r = q;
}
@@ -2178,7 +2228,7 @@ static int cancel_job(sd_bus *bus, char **args) {
}
static int need_daemon_reload(sd_bus *bus, const char *unit) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
const char *path;
int b, r;
@@ -2253,7 +2303,6 @@ static int unit_file_find_path(LookupPaths *lp, const char *unit_name, char **un
static int unit_find_paths(
sd_bus *bus,
const char *unit_name,
- bool avoid_bus_cache,
LookupPaths *lp,
char **fragment_path,
char ***dropin_paths) {
@@ -2274,9 +2323,9 @@ static int unit_find_paths(
assert(fragment_path);
assert(lp);
- 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;
+ if (!install_client_side() && !unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *unit_load_error = NULL;
_cleanup_free_ char *unit = NULL;
char *unit_load_error_name, *unit_load_error_message;
@@ -2391,7 +2440,7 @@ static int unit_find_paths(
}
static int check_one_unit(sd_bus *bus, const char *name, const char *good_states, bool quiet) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_free_ char *n = NULL, *state = NULL;
const char *path;
int r;
@@ -2448,7 +2497,7 @@ static int check_triggering_units(
sd_bus *bus,
const char *name) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_free_ char *path = NULL, *n = NULL, *state = NULL;
_cleanup_strv_free_ char **triggered_by = NULL;
bool print_warning_label = true;
@@ -2471,10 +2520,8 @@ static int check_triggering_units(
"LoadState",
&error,
&state);
- if (r < 0) {
- log_error("Failed to get load state of %s: %s", n, bus_error_message(&error, r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get load state of %s: %s", n, bus_error_message(&error, r));
if (streq(state, "masked"))
return 0;
@@ -2487,10 +2534,8 @@ static int check_triggering_units(
"TriggeredBy",
&error,
&triggered_by);
- if (r < 0) {
- log_error("Failed to get triggered by array of %s: %s", n, bus_error_message(&error, r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get triggered by array of %s: %s", n, bus_error_message(&error, r));
STRV_FOREACH(i, triggered_by) {
r = check_one_unit(bus, *i, "active\0reloading\0", true);
@@ -2556,7 +2601,7 @@ static int start_unit_one(
sd_bus_error *error,
BusWaitForJobs *w) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
const char *path;
int r;
@@ -2587,6 +2632,11 @@ static int start_unit_one(
verb = method_to_verb(method);
log_error("Failed to %s %s: %s", verb, name, bus_error_message(error, r));
+
+ if (!sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) &&
+ !sd_bus_error_has_name(error, BUS_ERROR_UNIT_MASKED))
+ log_error("See system logs and 'systemctl status %s' for details.", name);
+
return r;
}
@@ -2608,11 +2658,13 @@ static int start_unit_one(
}
static int expand_names(sd_bus *bus, char **names, const char* suffix, char ***ret) {
-
_cleanup_strv_free_ char **mangled = NULL, **globs = NULL;
char **name;
int r, i;
+ assert(bus);
+ assert(ret);
+
STRV_FOREACH(name, names) {
char *t;
@@ -2634,12 +2686,9 @@ static int expand_names(sd_bus *bus, char **names, const char* suffix, char ***r
/* Query the manager only if any of the names are a glob, since
* this is fairly expensive */
if (!strv_isempty(globs)) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_free_ UnitInfo *unit_infos = NULL;
- if (!bus)
- 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)
return r;
@@ -2687,24 +2736,28 @@ static enum action verb_to_action(const char *verb) {
return _ACTION_INVALID;
}
-static int start_unit(sd_bus *bus, char **args) {
+static int start_unit(int argc, char *argv[], void *userdata) {
_cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
const char *method, *mode, *one_name, *suffix = NULL;
_cleanup_strv_free_ char **names = NULL;
+ sd_bus *bus;
char **name;
int r = 0;
- assert(bus);
-
ask_password_agent_open_if_enabled();
polkit_agent_open_if_enabled();
+ r = acquire_bus(BUS_MANAGER, &bus);
+ if (r < 0)
+ return r;
+
if (arg_action == ACTION_SYSTEMCTL) {
enum action action;
- method = verb_to_method(args[0]);
- action = verb_to_action(args[0]);
- if (streq(args[0], "isolate")) {
+ method = verb_to_method(argv[0]);
+ action = verb_to_action(argv[0]);
+
+ if (streq(argv[0], "isolate")) {
mode = "isolate";
suffix = ".target";
} else
@@ -2724,9 +2777,9 @@ static int start_unit(sd_bus *bus, char **args) {
if (one_name)
names = strv_new(one_name, NULL);
else {
- r = expand_names(bus, args + 1, suffix, &names);
+ r = expand_names(bus, strv_skip(argv, 1), suffix, &names);
if (r < 0)
- log_error_errno(r, "Failed to expand names: %m");
+ return log_error_errno(r, "Failed to expand names: %m");
}
if (!arg_no_block) {
@@ -2736,7 +2789,7 @@ static int start_unit(sd_bus *bus, char **args) {
}
STRV_FOREACH(name, names) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int q;
q = start_unit_one(bus, method, *name, mode, &error, w);
@@ -2747,7 +2800,7 @@ static int start_unit(sd_bus *bus, char **args) {
if (!arg_no_block) {
int q;
- q = bus_wait_for_jobs(w, arg_quiet);
+ q = bus_wait_for_jobs(w, arg_quiet, arg_scope != UNIT_FILE_SYSTEM ? "--user" : NULL);
if (q < 0)
return q;
@@ -2761,18 +2814,55 @@ static int start_unit(sd_bus *bus, char **args) {
return r;
}
+static int logind_set_wall_message(void) {
+#ifdef HAVE_LOGIND
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ sd_bus *bus;
+ _cleanup_free_ char *m = NULL;
+ int r;
+
+ r = acquire_bus(BUS_FULL, &bus);
+ if (r < 0)
+ return r;
+
+ m = strv_join(arg_wall, " ");
+ if (!m)
+ return log_oom();
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "SetWallMessage",
+ &error,
+ NULL,
+ "sb",
+ m,
+ !arg_no_wall);
+
+ if (r < 0)
+ return log_warning_errno(r, "Failed to set wall message, ignoring: %s", bus_error_message(&error, r));
+
+#endif
+ return 0;
+}
+
/* Ask systemd-logind, which might grant access to unprivileged users
* through PolicyKit */
-static int reboot_with_logind(sd_bus *bus, enum action a) {
+static int logind_reboot(enum action a) {
#ifdef HAVE_LOGIND
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
const char *method, *description;
+ sd_bus *bus;
int r;
- if (!bus)
- return -EIO;
-
polkit_agent_open_if_enabled();
+ (void) logind_set_wall_message();
+
+ r = acquire_bus(BUS_FULL, &bus);
+ if (r < 0)
+ return r;
switch (a) {
@@ -2805,33 +2895,6 @@ static int reboot_with_logind(sd_bus *bus, enum action a) {
return -EINVAL;
}
- if (!strv_isempty(arg_wall)) {
- _cleanup_free_ char *m;
-
- m = strv_join(arg_wall, " ");
- if (!m)
- return log_oom();
-
- r = sd_bus_call_method(
- bus,
- "org.freedesktop.login1",
- "/org/freedesktop/login1",
- "org.freedesktop.login1.Manager",
- "SetWallMessage",
- &error,
- NULL,
- "sb",
- m,
- !arg_no_wall);
-
- if (r < 0) {
- log_warning_errno(r, "Failed to set wall message, ignoring: %s",
- bus_error_message(&error, r));
- sd_bus_error_free(&error);
- }
- }
-
-
r = sd_bus_call_method(
bus,
"org.freedesktop.login1",
@@ -2842,27 +2905,25 @@ static int reboot_with_logind(sd_bus *bus, enum action a) {
NULL,
"b", arg_ask_password);
if (r < 0)
- log_error("Failed to %s via logind: %s", description, bus_error_message(&error, r));
+ return log_error_errno(r, "Failed to %s via logind: %s", description, bus_error_message(&error, r));
- return r;
+ return 0;
#else
return -ENOSYS;
#endif
}
-static int check_inhibitors(sd_bus *bus, enum action a) {
+static int logind_check_inhibitors(enum action a) {
#ifdef HAVE_LOGIND
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_strv_free_ char **sessions = NULL;
const char *what, *who, *why, *mode;
uint32_t uid, pid;
+ sd_bus *bus;
unsigned c = 0;
char **s;
int r;
- if (!bus)
- return 0;
-
if (arg_ignore_inhibitors || arg_force > 0)
return 0;
@@ -2875,6 +2936,10 @@ static int check_inhibitors(sd_bus *bus, enum action a) {
if (!on_tty())
return 0;
+ r = acquire_bus(BUS_FULL, &bus);
+ if (r < 0)
+ return r;
+
r = sd_bus_call_method(
bus,
"org.freedesktop.login1",
@@ -2964,10 +3029,36 @@ static int check_inhibitors(sd_bus *bus, enum action a) {
#endif
}
-static int prepare_firmware_setup(sd_bus *bus) {
+static int logind_prepare_firmware_setup(void) {
#ifdef HAVE_LOGIND
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ sd_bus *bus;
+ int r;
+
+ r = acquire_bus(BUS_FULL, &bus);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "SetRebootToFirmwareSetup",
+ &error,
+ NULL,
+ "b", true);
+ if (r < 0)
+ return log_error_errno(r, "Cannot indicate to EFI to boot into setup mode: %s", bus_error_message(&error, r));
+
+ return 0;
+#else
+ log_error("Cannot remotely indicate to EFI to boot into setup mode.");
+ return -ENOSYS;
#endif
+}
+
+static int prepare_firmware_setup(void) {
int r;
if (!arg_firmware_setup)
@@ -2982,38 +3073,42 @@ static int prepare_firmware_setup(sd_bus *bus) {
return r;
}
-#ifdef HAVE_LOGIND
+ return logind_prepare_firmware_setup();
+}
+
+static int set_exit_code(uint8_t code) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ sd_bus *bus;
+ int r;
+
+ r = acquire_bus(BUS_MANAGER, &bus);
+ if (r < 0)
+ return r;
+
r = sd_bus_call_method(
bus,
- "org.freedesktop.login1",
- "/org/freedesktop/login1",
- "org.freedesktop.login1.Manager",
- "SetRebootToFirmwareSetup",
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "SetExitCode",
&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;
- }
+ "y", code);
+ if (r < 0)
+ return log_error_errno(r, "Failed to execute operation: %s", bus_error_message(&error, 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) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+static int start_special(int argc, char *argv[], void *userdata) {
enum action a;
int r;
- assert(args);
+ assert(argv);
- a = verb_to_action(args[0]);
+ a = verb_to_action(argv[0]);
- r = check_inhibitors(bus, a);
+ r = logind_check_inhibitors(a);
if (r < 0)
return r;
@@ -3022,39 +3117,29 @@ static int start_special(sd_bus *bus, char **args) {
return -EPERM;
}
- r = prepare_firmware_setup(bus);
+ r = prepare_firmware_setup();
if (r < 0)
return r;
- if (a == ACTION_REBOOT && args[1]) {
- r = update_reboot_param_file(args[1]);
+ if (a == ACTION_REBOOT && argc > 1) {
+ r = update_reboot_param_file(argv[1]);
if (r < 0)
return r;
- } else if (a == ACTION_EXIT && strv_length(args) > 1) {
- /* If the exit code is not given on the command line, don't
- * reset it to zero: just keep it as it might have been set
- * previously. */
- uint8_t code = 0;
- r = safe_atou8(args[1], &code);
- if (r < 0) {
- log_error("Invalid exit code.");
- return -EINVAL;
- }
+ } else if (a == ACTION_EXIT && argc > 1) {
+ uint8_t code;
- r = sd_bus_call_method(
- bus,
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "SetExitCode",
- &error,
- NULL,
- "y", code);
- if (r < 0) {
- log_error("Failed to execute operation: %s", bus_error_message(&error, r));
+ /* If the exit code is not given on the command line,
+ * don't reset it to zero: just keep it as it might
+ * have been set previously. */
+
+ r = safe_atou8(argv[1], &code);
+ if (r < 0)
+ return log_error_errno(r, "Invalid exit code.");
+
+ r = set_exit_code(code);
+ if (r < 0)
return r;
- }
}
if (arg_force >= 2 &&
@@ -3071,39 +3156,37 @@ static int start_special(sd_bus *bus, char **args) {
ACTION_REBOOT,
ACTION_KEXEC,
ACTION_EXIT))
- return daemon_reload(bus, args);
+ return daemon_reload(argc, argv, userdata);
- /* first try logind, to allow authentication with polkit */
- if (geteuid() != 0 &&
- IN_SET(a,
+ /* First try logind, to allow authentication with polkit */
+ if (IN_SET(a,
ACTION_POWEROFF,
ACTION_REBOOT,
ACTION_SUSPEND,
ACTION_HIBERNATE,
ACTION_HYBRID_SLEEP)) {
- r = reboot_with_logind(bus, a);
+ r = logind_reboot(a);
if (r >= 0)
return r;
if (IN_SET(r, -EOPNOTSUPP, -EINPROGRESS))
/* requested operation is not supported or already in progress */
return r;
- /* on all other errors, try low-level operation */
- }
- r = start_unit(bus, args);
- if (r == EXIT_SUCCESS)
- warn_wall(a);
+ /* On all other errors, try low-level operation */
+ }
- return r;
+ return start_unit(argc, argv, userdata);
}
-static int check_unit_generic(sd_bus *bus, int code, const char *good_states, char **args) {
+static int check_unit_generic(int code, const char *good_states, char **args) {
_cleanup_strv_free_ char **names = NULL;
+ sd_bus *bus;
char **name;
int r;
- assert(bus);
- assert(args);
+ r = acquire_bus(BUS_MANAGER, &bus);
+ if (r < 0)
+ return r;
r = expand_names(bus, args, NULL, &names);
if (r < 0)
@@ -3122,25 +3205,27 @@ static int check_unit_generic(sd_bus *bus, int code, const char *good_states, ch
return r;
}
-static int check_unit_active(sd_bus *bus, char **args) {
+static int check_unit_active(int argc, char *argv[], void *userdata) {
/* According to LSB: 3, "program is not running" */
- return check_unit_generic(bus, 3, "active\0reloading\0", args + 1);
+ return check_unit_generic(3, "active\0reloading\0", strv_skip(argv, 1));
}
-static int check_unit_failed(sd_bus *bus, char **args) {
- return check_unit_generic(bus, 1, "failed\0", args + 1);
+static int check_unit_failed(int argc, char *argv[], void *userdata) {
+ return check_unit_generic(1, "failed\0", strv_skip(argv, 1));
}
-static int kill_unit(sd_bus *bus, char **args) {
+static int kill_unit(int argc, char *argv[], void *userdata) {
_cleanup_strv_free_ char **names = NULL;
char *kill_who = NULL, **name;
+ sd_bus *bus;
int r, q;
- assert(bus);
- assert(args);
-
polkit_agent_open_if_enabled();
+ r = acquire_bus(BUS_MANAGER, &bus);
+ if (r < 0)
+ return r;
+
if (!arg_kill_who)
arg_kill_who = "all";
@@ -3148,12 +3233,12 @@ static int kill_unit(sd_bus *bus, char **args) {
if (streq(arg_job_mode, "fail"))
kill_who = strjoina(arg_kill_who, "-fail", NULL);
- r = expand_names(bus, args + 1, NULL, &names);
+ r = expand_names(bus, strv_skip(argv, 1), NULL, &names);
if (r < 0)
- log_error_errno(r, "Failed to expand names: %m");
+ return log_error_errno(r, "Failed to expand names: %m");
STRV_FOREACH(name, names) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
q = sd_bus_call_method(
bus,
@@ -3165,7 +3250,7 @@ static int kill_unit(sd_bus *bus, char **args) {
NULL,
"ssi", *names, kill_who ? kill_who : arg_kill_who, arg_signal);
if (q < 0) {
- log_error("Failed to kill unit %s: %s", *names, bus_error_message(&error, q));
+ log_error_errno(q, "Failed to kill unit %s: %s", *names, bus_error_message(&error, q));
if (r == 0)
r = q;
}
@@ -3281,6 +3366,7 @@ typedef struct UnitStatusInfo {
usec_t inactive_enter_timestamp;
bool need_daemon_reload;
+ bool transient;
/* Service */
pid_t main_pid;
@@ -3380,7 +3466,7 @@ static void print_status_info(
path = i->source_path ? i->source_path : i->fragment_path;
- if (i->load_error)
+ if (i->load_error != 0)
printf(" Loaded: %s%s%s (Reason: %s)\n",
on, strna(i->load_state), off, i->load_error);
else if (path && !isempty(i->unit_file_state) && !isempty(i->unit_file_preset))
@@ -3396,6 +3482,9 @@ static void print_status_info(
printf(" Loaded: %s%s%s\n",
on, strna(i->load_state), off);
+ if (i->transient)
+ printf("Transient: yes\n");
+
if (!strv_isempty(i->dropin_paths)) {
_cleanup_free_ char *dir = NULL;
bool last = false;
@@ -3407,7 +3496,8 @@ static void print_status_info(
dir = mfree(dir);
- if (path_get_parent(*dropin, &dir) < 0) {
+ dir = dirname_malloc(*dropin);
+ if (!dir) {
log_oom();
return;
}
@@ -3456,7 +3546,7 @@ static void print_status_info(
printf("Condition: start %scondition failed%s at %s%s%s\n",
ansi_highlight_yellow(), ansi_normal(),
- s2, s1 ? "; " : "", s1 ? s1 : "");
+ s2, s1 ? "; " : "", strempty(s1));
if (i->failed_condition_trigger)
printf(" none of the trigger conditions were met\n");
else if (i->failed_condition)
@@ -3472,7 +3562,7 @@ static void print_status_info(
printf(" Assert: start %sassertion failed%s at %s%s%s\n",
ansi_highlight_red(), ansi_normal(),
- s2, s1 ? "; " : "", s1 ? s1 : "");
+ s2, s1 ? "; " : "", strempty(s1));
if (i->failed_assert_trigger)
printf(" none of the trigger assertions were met\n");
else if (i->failed_assert)
@@ -3759,6 +3849,8 @@ static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo *
i->condition_result = b;
else if (streq(name, "AssertResult"))
i->assert_result = b;
+ else if (streq(name, "Transient"))
+ i->transient = b;
break;
}
@@ -3861,13 +3953,13 @@ static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo *
info->name = strdup(name);
if (!info->name)
- log_oom();
+ return log_oom();
LIST_PREPEND(exec, i->exec, info);
info = new0(ExecStatusInfo, 1);
if (!info)
- log_oom();
+ return log_oom();
}
if (r < 0)
@@ -4308,8 +4400,8 @@ static int show_one(
bool *new_line,
bool *ellipsized) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
UnitStatusInfo info = {
.memory_current = (uint64_t) -1,
.memory_limit = (uint64_t) -1,
@@ -4425,8 +4517,8 @@ static int get_unit_dbus_path_by_pid(
uint32_t pid,
char **unit) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
char *u;
int r;
@@ -4439,10 +4531,8 @@ static int get_unit_dbus_path_by_pid(
&error,
&reply,
"u", pid);
- if (r < 0) {
- log_error("Failed to get unit for PID %"PRIu32": %s", pid, bus_error_message(&error, r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get unit for PID %"PRIu32": %s", pid, bus_error_message(&error, r));
r = sd_bus_message_read(reply, "o", &u);
if (r < 0)
@@ -4463,7 +4553,7 @@ static int show_all(
bool *new_line,
bool *ellipsized) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_free_ UnitInfo *unit_infos = NULL;
const UnitInfo *u;
unsigned c;
@@ -4551,19 +4641,24 @@ static int show_system_status(sd_bus *bus) {
return 0;
}
-static int show(sd_bus *bus, char **args) {
- bool show_properties, show_status, new_line = false;
+static int show(int argc, char *argv[], void *userdata) {
+ bool show_properties, show_status, show_help, new_line = false;
bool ellipsized = false;
int r, ret = 0;
+ sd_bus *bus;
- assert(bus);
- assert(args);
+ assert(argv);
- show_properties = streq(args[0], "show");
- show_status = streq(args[0], "status");
+ show_properties = streq(argv[0], "show");
+ show_status = streq(argv[0], "status");
+ show_help = streq(argv[0], "help");
- if (show_properties)
- pager_open_if_enabled();
+ if (show_help && argc <= 1) {
+ log_error("This command expects one or more unit names. Did you mean --help?");
+ return -EINVAL;
+ }
+
+ pager_open_if_enabled();
if (show_status)
/* Increase max number of open files to 16K if we can, we
@@ -4571,24 +4666,27 @@ static int show(sd_bus *bus, char **args) {
* be split up into many files. */
setrlimit_closest(RLIMIT_NOFILE, &RLIMIT_MAKE_CONST(16384));
- /* If no argument is specified inspect the manager itself */
+ r = acquire_bus(BUS_MANAGER, &bus);
+ if (r < 0)
+ return r;
- if (show_properties && strv_length(args) <= 1)
- return show_one(args[0], bus, "/org/freedesktop/systemd1", show_properties, &new_line, &ellipsized);
+ /* If no argument is specified inspect the manager itself */
+ if (show_properties && argc <= 1)
+ return show_one(argv[0], bus, "/org/freedesktop/systemd1", show_properties, &new_line, &ellipsized);
- if (show_status && strv_length(args) <= 1) {
+ if (show_status && argc <= 1) {
pager_open_if_enabled();
show_system_status(bus);
new_line = true;
if (arg_all)
- ret = show_all(args[0], bus, false, &new_line, &ellipsized);
+ ret = show_all(argv[0], bus, false, &new_line, &ellipsized);
} else {
_cleanup_free_ char **patterns = NULL;
char **name;
- STRV_FOREACH(name, args + 1) {
+ STRV_FOREACH(name, strv_skip(argv, 1)) {
_cleanup_free_ char *unit = NULL;
uint32_t id;
@@ -4611,8 +4709,7 @@ static int show(sd_bus *bus, char **args) {
}
}
- r = show_one(args[0], bus, unit, show_properties,
- &new_line, &ellipsized);
+ r = show_one(argv[0], bus, unit, show_properties, &new_line, &ellipsized);
if (r < 0)
return r;
else if (r > 0 && ret == 0)
@@ -4624,7 +4721,7 @@ static int show(sd_bus *bus, char **args) {
r = expand_names(bus, patterns, NULL, &names);
if (r < 0)
- log_error_errno(r, "Failed to expand names: %m");
+ return log_error_errno(r, "Failed to expand names: %m");
STRV_FOREACH(name, names) {
_cleanup_free_ char *unit;
@@ -4633,8 +4730,7 @@ static int show(sd_bus *bus, char **args) {
if (!unit)
return log_oom();
- r = show_one(args[0], bus, unit, show_properties,
- &new_line, &ellipsized);
+ r = show_one(argv[0], bus, unit, show_properties, &new_line, &ellipsized);
if (r < 0)
return r;
else if (r > 0 && ret == 0)
@@ -4694,19 +4790,18 @@ static int cat_file(const char *filename, bool newline) {
return copy_bytes(fd, STDOUT_FILENO, (uint64_t) -1, false);
}
-static int cat(sd_bus *bus, char **args) {
+static int cat(int argc, char *argv[], void *userdata) {
_cleanup_free_ char *user_home = NULL;
_cleanup_free_ char *user_runtime = NULL;
_cleanup_lookup_paths_free_ LookupPaths lp = {};
_cleanup_strv_free_ char **names = NULL;
char **name;
- bool first = true, avoid_bus_cache;
+ sd_bus *bus;
+ bool first = true;
int r;
- assert(args);
-
if (arg_transport != BUS_TRANSPORT_LOCAL) {
- log_error("Cannot remotely cat units");
+ log_error("Cannot remotely cat units.");
return -EINVAL;
}
@@ -4714,11 +4809,13 @@ static int cat(sd_bus *bus, char **args) {
if (r < 0)
return r;
- r = expand_names(bus, args + 1, NULL, &names);
+ r = acquire_bus(BUS_MANAGER, &bus);
if (r < 0)
- return log_error_errno(r, "Failed to expand names: %m");
+ return r;
- avoid_bus_cache = !bus || avoid_bus();
+ r = expand_names(bus, strv_skip(argv, 1), NULL, &names);
+ if (r < 0)
+ return log_error_errno(r, "Failed to expand names: %m");
pager_open_if_enabled();
@@ -4727,7 +4824,7 @@ static int cat(sd_bus *bus, char **args) {
_cleanup_strv_free_ char **dropin_paths = NULL;
char **path;
- r = unit_find_paths(bus, *name, avoid_bus_cache, &lp, &fragment_path, &dropin_paths);
+ r = unit_find_paths(bus, *name, &lp, &fragment_path, &dropin_paths);
if (r < 0)
return r;
else if (r == 0)
@@ -4754,15 +4851,20 @@ static int cat(sd_bus *bus, char **args) {
return 0;
}
-static int set_property(sd_bus *bus, char **args) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+static int set_property(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_free_ char *n = NULL;
+ sd_bus *bus;
char **i;
int r;
polkit_agent_open_if_enabled();
+ r = acquire_bus(BUS_MANAGER, &bus);
+ if (r < 0)
+ return r;
+
r = sd_bus_message_new_method_call(
bus,
&m,
@@ -4773,7 +4875,7 @@ static int set_property(sd_bus *bus, char **args) {
if (r < 0)
return bus_log_create_error(r);
- r = unit_name_mangle(args[1], UNIT_NAME_NOGLOB, &n);
+ r = unit_name_mangle(argv[1], UNIT_NAME_NOGLOB, &n);
if (r < 0)
return log_error_errno(r, "Failed to mangle unit name: %m");
@@ -4785,7 +4887,7 @@ static int set_property(sd_bus *bus, char **args) {
if (r < 0)
return bus_log_create_error(r);
- STRV_FOREACH(i, args + 2) {
+ STRV_FOREACH(i, strv_skip(argv, 2)) {
r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
if (r < 0)
return bus_log_create_error(r);
@@ -4804,112 +4906,23 @@ static int set_property(sd_bus *bus, char **args) {
return bus_log_create_error(r);
r = sd_bus_call(bus, m, 0, &error, NULL);
- if (r < 0) {
- log_error("Failed to set unit properties on %s: %s", n, bus_error_message(&error, r));
- return r;
- }
-
- return 0;
-}
-
-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 *reply = NULL;
- _cleanup_free_ char *n = NULL, *id = NULL;
- const char *path;
- int r;
-
- polkit_agent_open_if_enabled();
-
- 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();
- }
-
- r = sd_bus_call_method(
- bus,
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "CreateSnapshot",
- &error,
- &reply,
- "sb", n, false);
- if (r < 0) {
- log_error("Failed to create snapshot: %s", bus_error_message(&error, r));
- return r;
- }
-
- r = sd_bus_message_read(reply, "o", &path);
if (r < 0)
- return bus_log_parse_error(r);
-
- r = sd_bus_get_property_string(
- bus,
- "org.freedesktop.systemd1",
- path,
- "org.freedesktop.systemd1.Unit",
- "Id",
- &error,
- &id);
- if (r < 0) {
- log_error("Failed to get ID of snapshot: %s", bus_error_message(&error, r));
- return r;
- }
-
- if (!arg_quiet)
- puts(id);
+ return log_error_errno(r, "Failed to set unit properties on %s: %s", n, bus_error_message(&error, r));
return 0;
}
-static int delete_snapshot(sd_bus *bus, char **args) {
- _cleanup_strv_free_ char **names = NULL;
- char **name;
+static int daemon_reload(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ const char *method;
+ sd_bus *bus;
int r;
- assert(args);
-
polkit_agent_open_if_enabled();
- r = expand_names(bus, args + 1, ".snapshot", &names);
+ r = acquire_bus(BUS_MANAGER, &bus);
if (r < 0)
- log_error_errno(r, "Failed to expand names: %m");
-
- STRV_FOREACH(name, names) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- int q;
-
- q = sd_bus_call_method(
- bus,
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "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)
- r = q;
- }
- }
-
- return r;
-}
-
-static int daemon_reload(sd_bus *bus, char **args) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- const char *method;
- int r;
-
- polkit_agent_open_if_enabled();
+ return r;
if (arg_action == ACTION_RELOAD)
method = "Reload";
@@ -4919,15 +4932,15 @@ static int daemon_reload(sd_bus *bus, char **args) {
assert(arg_action == ACTION_SYSTEMCTL);
method =
- streq(args[0], "clear-jobs") ||
- streq(args[0], "cancel") ? "ClearJobs" :
- streq(args[0], "daemon-reexec") ? "Reexecute" :
- streq(args[0], "reset-failed") ? "ResetFailed" :
- streq(args[0], "halt") ? "Halt" :
- streq(args[0], "poweroff") ? "PowerOff" :
- streq(args[0], "reboot") ? "Reboot" :
- streq(args[0], "kexec") ? "KExec" :
- streq(args[0], "exit") ? "Exit" :
+ streq(argv[0], "clear-jobs") ||
+ streq(argv[0], "cancel") ? "ClearJobs" :
+ streq(argv[0], "daemon-reexec") ? "Reexecute" :
+ streq(argv[0], "reset-failed") ? "ResetFailed" :
+ streq(argv[0], "halt") ? "Halt" :
+ streq(argv[0], "poweroff") ? "PowerOff" :
+ streq(argv[0], "reboot") ? "Reboot" :
+ streq(argv[0], "kexec") ? "KExec" :
+ streq(argv[0], "exit") ? "Exit" :
/* "daemon-reload" */ "Reload";
}
@@ -4949,27 +4962,32 @@ static int daemon_reload(sd_bus *bus, char **args) {
* reply */
r = 0;
else if (r < 0)
- log_error("Failed to execute operation: %s", bus_error_message(&error, r));
+ return log_error_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r));
return r < 0 ? r : 0;
}
-static int reset_failed(sd_bus *bus, char **args) {
+static int reset_failed(int argc, char *argv[], void *userdata) {
_cleanup_strv_free_ char **names = NULL;
+ sd_bus *bus;
char **name;
int r, q;
- if (strv_length(args) <= 1)
- return daemon_reload(bus, args);
+ if (argc <= 1)
+ return daemon_reload(argc, argv, userdata);
polkit_agent_open_if_enabled();
- r = expand_names(bus, args + 1, NULL, &names);
+ r = acquire_bus(BUS_MANAGER, &bus);
if (r < 0)
- log_error_errno(r, "Failed to expand names: %m");
+ return r;
+
+ r = expand_names(bus, strv_skip(argv, 1), NULL, &names);
+ if (r < 0)
+ return log_error_errno(r, "Failed to expand names: %m");
STRV_FOREACH(name, names) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
q = sd_bus_call_method(
bus,
@@ -4981,7 +4999,7 @@ static int reset_failed(sd_bus *bus, char **args) {
NULL,
"s", *name);
if (q < 0) {
- log_error("Failed to reset failed state of unit %s: %s", *name, bus_error_message(&error, q));
+ log_error_errno(q, "Failed to reset failed state of unit %s: %s", *name, bus_error_message(&error, q));
if (r == 0)
r = q;
}
@@ -4990,14 +5008,19 @@ static int reset_failed(sd_bus *bus, char **args) {
return r;
}
-static int show_environment(sd_bus *bus, char **args) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+static int show_environment(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
const char *text;
+ sd_bus *bus;
int r;
pager_open_if_enabled();
+ r = acquire_bus(BUS_MANAGER, &bus);
+ if (r < 0)
+ return r;
+
r = sd_bus_get_property(
bus,
"org.freedesktop.systemd1",
@@ -5007,10 +5030,8 @@ static int show_environment(sd_bus *bus, char **args) {
&error,
&reply,
"as");
- if (r < 0) {
- log_error("Failed to get environment: %s", bus_error_message(&error, r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get environment: %s", bus_error_message(&error, r));
r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "s");
if (r < 0)
@@ -5028,23 +5049,27 @@ static int show_environment(sd_bus *bus, char **args) {
return 0;
}
-static int switch_root(sd_bus *bus, char **args) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+static int switch_root(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_free_ char *cmdline_init = NULL;
const char *root, *init;
- unsigned l;
+ sd_bus *bus;
int r;
- l = strv_length(args);
- if (l < 2 || l > 3) {
+ if (arg_transport != BUS_TRANSPORT_LOCAL) {
+ log_error("Cannot switch root remotely.");
+ return -EINVAL;
+ }
+
+ if (argc < 2 || argc > 3) {
log_error("Wrong number of arguments.");
return -EINVAL;
}
- root = args[1];
+ root = argv[1];
- if (l >= 3)
- init = args[2];
+ if (argc >= 3)
+ init = argv[2];
else {
r = parse_env_file("/proc/cmdline", WHITESPACE,
"init", &cmdline_init,
@@ -5070,6 +5095,10 @@ static int switch_root(sd_bus *bus, char **args) {
init = NULL;
}
+ r = acquire_bus(BUS_MANAGER, &bus);
+ if (r < 0)
+ return r;
+
log_debug("Switching root - root: %s; init: %s", root, strna(init));
r = sd_bus_call_method(
@@ -5081,26 +5110,29 @@ static int switch_root(sd_bus *bus, char **args) {
&error,
NULL,
"ss", root, init);
- if (r < 0) {
- log_error("Failed to switch root: %s", bus_error_message(&error, r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to switch root: %s", bus_error_message(&error, r));
return 0;
}
-static int set_environment(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;
+static int set_environment(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
const char *method;
+ sd_bus *bus;
int r;
- assert(bus);
- assert(args);
+ assert(argc > 1);
+ assert(argv);
polkit_agent_open_if_enabled();
- method = streq(args[0], "set-environment")
+ r = acquire_bus(BUS_MANAGER, &bus);
+ if (r < 0)
+ return r;
+
+ method = streq(argv[0], "set-environment")
? "SetEnvironment"
: "UnsetEnvironment";
@@ -5114,29 +5146,29 @@ static int set_environment(sd_bus *bus, char **args) {
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_append_strv(m, args + 1);
+ r = sd_bus_message_append_strv(m, strv_skip(argv, 1));
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_call(bus, m, 0, &error, NULL);
- if (r < 0) {
- log_error("Failed to set environment: %s", bus_error_message(&error, r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to set environment: %s", bus_error_message(&error, r));
return 0;
}
-static int import_environment(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;
+static int import_environment(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+ sd_bus *bus;
int r;
- assert(bus);
- assert(args);
-
polkit_agent_open_if_enabled();
+ r = acquire_bus(BUS_MANAGER, &bus);
+ if (r < 0)
+ return r;
+
r = sd_bus_message_new_method_call(
bus,
&m,
@@ -5147,7 +5179,7 @@ static int import_environment(sd_bus *bus, char **args) {
if (r < 0)
return bus_log_create_error(r);
- if (strv_isempty(args + 1))
+ if (argc < 2)
r = sd_bus_message_append_strv(m, environ);
else {
char **a, **b;
@@ -5156,7 +5188,7 @@ static int import_environment(sd_bus *bus, char **args) {
if (r < 0)
return bus_log_create_error(r);
- STRV_FOREACH(a, args + 1) {
+ STRV_FOREACH(a, strv_skip(argv, 1)) {
if (!env_name_is_valid(*a)) {
log_error("Not a valid environment variable name: %s", *a);
@@ -5184,10 +5216,8 @@ static int import_environment(sd_bus *bus, char **args) {
return bus_log_create_error(r);
r = sd_bus_call(bus, m, 0, &error, NULL);
- if (r < 0) {
- log_error("Failed to import environment: %s", bus_error_message(&error, r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to import environment: %s", bus_error_message(&error, r));
return 0;
}
@@ -5202,6 +5232,9 @@ static int enable_sysv_units(const char *verb, char **args) {
if (arg_scope != UNIT_FILE_SYSTEM)
return 0;
+ if (getenv_bool("SYSTEMCTL_SKIP_SYSV") > 0)
+ return 0;
+
if (!STR_IN_SET(verb,
"enable",
"disable",
@@ -5290,13 +5323,13 @@ static int enable_sysv_units(const char *verb, char **args) {
(void) reset_signal_mask();
execv(argv[0], (char**) argv);
- log_error("Failed to execute %s: %m", argv[0]);
+ log_error_errno(r, "Failed to execute %s: %m", argv[0]);
_exit(EXIT_FAILURE);
}
j = wait_for_terminate(pid, &status);
if (j < 0) {
- log_error_errno(r, "Failed to wait for child: %m");
+ log_error_errno(j, "Failed to wait for child: %m");
return j;
}
@@ -5366,18 +5399,18 @@ static int mangle_names(char **original_names, char ***mangled_names) {
return 0;
}
-static int enable_unit(sd_bus *bus, char **args) {
+static int enable_unit(int argc, char *argv[], void *userdata) {
_cleanup_strv_free_ char **names = NULL;
- const char *verb = args[0];
+ const char *verb = argv[0];
UnitFileChange *changes = NULL;
unsigned n_changes = 0;
int carries_install_info = -1;
int r;
- if (!args[1])
+ if (!argv[1])
return 0;
- r = mangle_names(args+1, &names);
+ r = mangle_names(strv_skip(argv, 1), &names);
if (r < 0)
return r;
@@ -5390,7 +5423,7 @@ static int enable_unit(sd_bus *bus, char **args) {
if (strv_isempty(names))
return 0;
- if (!bus || avoid_bus()) {
+ if (install_client_side()) {
if (streq(verb, "enable")) {
r = unit_file_enable(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes);
carries_install_info = r;
@@ -5411,24 +5444,29 @@ static int enable_unit(sd_bus *bus, char **args) {
else
assert_not_reached("Unknown verb");
- if (r < 0) {
- log_error_errno(r, "Operation failed: %m");
- goto finish;
- }
+ if (r == -ESHUTDOWN)
+ return log_error_errno(r, "Unit file is masked.");
+ if (r < 0)
+ return log_error_errno(r, "Operation failed: %m");
if (!arg_quiet)
dump_unit_file_changes(changes, n_changes);
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_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int expect_carries_install_info = false;
bool send_force = true, send_preset_mode = false;
const char *method;
+ sd_bus *bus;
polkit_agent_open_if_enabled();
+ r = acquire_bus(BUS_MANAGER, &bus);
+ if (r < 0)
+ return r;
+
if (streq(verb, "enable")) {
method = "EnableUnitFiles";
expect_carries_install_info = true;
@@ -5488,10 +5526,8 @@ static int enable_unit(sd_bus *bus, char **args) {
}
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;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r));
if (expect_carries_install_info) {
r = sd_bus_message_read(reply, "b", &carries_install_info);
@@ -5505,7 +5541,7 @@ static int enable_unit(sd_bus *bus, char **args) {
/* Try to reload if enabled */
if (!arg_no_reload)
- r = daemon_reload(bus, args);
+ r = daemon_reload(argc, argv, userdata);
else
r = 0;
}
@@ -5521,16 +5557,21 @@ 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")) {
+ if (arg_now && n_changes > 0 && STR_IN_SET(argv[0], "enable", "disable", "mask")) {
char *new_args[n_changes + 2];
+ sd_bus *bus;
unsigned i;
- new_args[0] = streq(args[0], "enable") ? (char *)"start" : (char *)"stop";
+ r = acquire_bus(BUS_MANAGER, &bus);
+ if (r < 0)
+ goto finish;
+
+ new_args[0] = (char*) (streq(argv[0], "enable") ? "start" : "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);
+ r = start_unit(strv_length(new_args), new_args, userdata);
}
finish:
@@ -5539,21 +5580,21 @@ finish:
return r;
}
-static int add_dependency(sd_bus *bus, char **args) {
+static int add_dependency(int argc, char *argv[], void *userdata) {
_cleanup_strv_free_ char **names = NULL;
_cleanup_free_ char *target = NULL;
- const char *verb = args[0];
+ const char *verb = argv[0];
UnitDependency dep;
int r = 0;
- if (!args[1])
+ if (!argv[1])
return 0;
- r = unit_name_mangle_with_suffix(args[1], UNIT_NAME_NOGLOB, ".target", &target);
+ r = unit_name_mangle_with_suffix(argv[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);
+ r = mangle_names(strv_skip(argv, 2), &names);
if (r < 0)
return r;
@@ -5564,12 +5605,13 @@ static int add_dependency(sd_bus *bus, char **args) {
else
assert_not_reached("Unknown verb");
- if (!bus || avoid_bus()) {
+ if (install_client_side()) {
UnitFileChange *changes = NULL;
unsigned n_changes = 0;
r = unit_file_add_dependency(arg_scope, arg_runtime, arg_root, names, target, dep, arg_force, &changes, &n_changes);
-
+ if (r == -ESHUTDOWN)
+ return log_error_errno(r, "Unit file is masked.");
if (r < 0)
return log_error_errno(r, "Can't add dependency: %m");
@@ -5579,11 +5621,16 @@ static int add_dependency(sd_bus *bus, char **args) {
unit_file_changes_free(changes, n_changes);
} 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_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ sd_bus *bus;
polkit_agent_open_if_enabled();
+ r = acquire_bus(BUS_MANAGER, &bus);
+ if (r < 0)
+ return r;
+
r = sd_bus_message_new_method_call(
bus,
&m,
@@ -5603,17 +5650,15 @@ static int add_dependency(sd_bus *bus, char **args) {
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;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r));
r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, NULL, NULL);
if (r < 0)
return r;
if (!arg_no_reload)
- r = daemon_reload(bus, args);
+ r = daemon_reload(argc, argv, userdata);
else
r = 0;
}
@@ -5621,12 +5666,12 @@ static int add_dependency(sd_bus *bus, char **args) {
return r;
}
-static int preset_all(sd_bus *bus, char **args) {
+static int preset_all(int argc, char *argv[], void *userdata) {
UnitFileChange *changes = NULL;
unsigned n_changes = 0;
int r;
- if (!bus || avoid_bus()) {
+ if (install_client_side()) {
r = unit_file_preset_all(arg_scope, arg_runtime, arg_root, arg_preset_mode, arg_force, &changes, &n_changes);
if (r < 0) {
@@ -5640,11 +5685,16 @@ static int preset_all(sd_bus *bus, char **args) {
r = 0;
} else {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ sd_bus *bus;
polkit_agent_open_if_enabled();
+ r = acquire_bus(BUS_MANAGER, &bus);
+ if (r < 0)
+ return r;
+
r = sd_bus_call_method(
bus,
"org.freedesktop.systemd1",
@@ -5657,17 +5707,15 @@ static int preset_all(sd_bus *bus, char **args) {
unit_file_preset_mode_to_string(arg_preset_mode),
arg_runtime,
arg_force);
- if (r < 0) {
- log_error("Failed to execute operation: %s", bus_error_message(&error, r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r));
r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, NULL, NULL);
if (r < 0)
return r;
if (!arg_no_reload)
- r = daemon_reload(bus, args);
+ r = daemon_reload(argc, argv, userdata);
else
r = 0;
}
@@ -5678,31 +5726,30 @@ finish:
return r;
}
-static int unit_is_enabled(sd_bus *bus, char **args) {
+static int unit_is_enabled(int argc, char *argv[], void *userdata) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_strv_free_ char **names = NULL;
bool enabled;
char **name;
int r;
- r = mangle_names(args+1, &names);
+ r = mangle_names(strv_skip(argv, 1), &names);
if (r < 0)
return r;
- r = enable_sysv_units(args[0], names);
+ r = enable_sysv_units(argv[0], names);
if (r < 0)
return r;
enabled = r > 0;
- if (!bus || avoid_bus()) {
+ if (install_client_side()) {
STRV_FOREACH(name, names) {
UnitFileState state;
- state = unit_file_get_state(arg_scope, arg_root, *name);
- if (state < 0)
+ r = unit_file_get_state(arg_scope, arg_root, *name, &state);
+ if (r < 0)
return log_error_errno(state, "Failed to get unit file state for %s: %m", *name);
if (IN_SET(state,
@@ -5717,8 +5764,15 @@ static int unit_is_enabled(sd_bus *bus, char **args) {
}
} else {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ sd_bus *bus;
+
+ r = acquire_bus(BUS_MANAGER, &bus);
+ if (r < 0)
+ return r;
+
STRV_FOREACH(name, names) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
const char *s;
r = sd_bus_call_method(
@@ -5730,10 +5784,8 @@ static int unit_is_enabled(sd_bus *bus, char **args) {
&error,
&reply,
"s", *name);
- if (r < 0) {
- log_error("Failed to get unit file state for %s: %s", *name, bus_error_message(&error, r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get unit file state for %s: %s", *name, bus_error_message(&error, r));
r = sd_bus_message_read(reply, "s", &s);
if (r < 0)
@@ -5750,10 +5802,21 @@ static int unit_is_enabled(sd_bus *bus, char **args) {
return !enabled;
}
-static int is_system_running(sd_bus *bus, char **args) {
+static int is_system_running(int argc, char *argv[], void *userdata) {
_cleanup_free_ char *state = NULL;
+ sd_bus *bus;
int r;
+ if (arg_transport == BUS_TRANSPORT_LOCAL && !sd_booted()) {
+ if (!arg_quiet)
+ puts("offline");
+ return EXIT_FAILURE;
+ }
+
+ r = acquire_bus(BUS_MANAGER, &bus);
+ if (r < 0)
+ return r;
+
r = sd_bus_get_property_string(
bus,
"org.freedesktop.systemd1",
@@ -5775,7 +5838,7 @@ static int is_system_running(sd_bus *bus, char **args) {
}
static int create_edit_temp_file(const char *new_path, const char *original_path, char **ret_tmp_fn) {
- char *t;
+ _cleanup_free_ char *t = NULL;
int r;
assert(new_path);
@@ -5787,27 +5850,21 @@ static int create_edit_temp_file(const char *new_path, const char *original_path
return log_error_errno(r, "Failed to determine temporary filename for \"%s\": %m", new_path);
r = mkdir_parents(new_path, 0755);
- if (r < 0) {
- log_error_errno(r, "Failed to create directories for \"%s\": %m", new_path);
- free(t);
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to create directories for \"%s\": %m", new_path);
r = copy_file(original_path, t, 0, 0644, 0);
if (r == -ENOENT) {
+
r = touch(t);
- if (r < 0) {
- log_error_errno(r, "Failed to create temporary file \"%s\": %m", t);
- free(t);
- return r;
- }
- } else if (r < 0) {
- log_error_errno(r, "Failed to copy \"%s\" to \"%s\": %m", original_path, t);
- free(t);
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to create temporary file \"%s\": %m", t);
+
+ } else if (r < 0)
+ return log_error_errno(r, "Failed to copy \"%s\" to \"%s\": %m", original_path, t);
*ret_tmp_fn = t;
+ t = NULL;
return 0;
}
@@ -5815,6 +5872,9 @@ static int create_edit_temp_file(const char *new_path, const char *original_path
static int get_file_to_edit(const char *name, const char *user_home, const char *user_runtime, char **ret_path) {
_cleanup_free_ char *path = NULL, *path2 = NULL, *run = NULL;
+ assert(name);
+ assert(ret_path);
+
switch (arg_scope) {
case UNIT_FILE_SYSTEM:
path = path_join(arg_root, SYSTEM_CONFIG_UNIT_PATH, name);
@@ -5845,12 +5905,16 @@ static int get_file_to_edit(const char *name, const char *user_home, const char
return log_oom();
if (arg_runtime) {
- if (access(path, F_OK) >= 0)
- 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 overridden by \"%s\" anyway.",
- run, path2);
+ if (access(path, F_OK) >= 0) {
+ log_error("Refusing to create \"%s\" because it would be overridden by \"%s\" anyway.", run, path);
+ return -EEXIST;
+ }
+
+ if (path2 && access(path2, F_OK) >= 0) {
+ log_error("Refusing to create \"%s\" because it would be overridden by \"%s\" anyway.", run, path2);
+ return -EEXIST;
+ }
+
*ret_path = run;
run = NULL;
} else {
@@ -5862,8 +5926,7 @@ static int get_file_to_edit(const char *name, const char *user_home, const char
}
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;
+ char *tmp_new_path, *tmp_tmp_path, *ending;
int r;
assert(unit_name);
@@ -5895,8 +5958,7 @@ static int unit_file_create_copy(
char **ret_new_path,
char **ret_tmp_path) {
- char *tmp_new_path;
- char *tmp_tmp_path;
+ char *tmp_new_path, *tmp_tmp_path;
int r;
assert(fragment_path);
@@ -5943,10 +6005,8 @@ static int run_editor(char **paths) {
assert(paths);
pid = fork();
- if (pid < 0) {
- log_error_errno(errno, "Failed to fork: %m");
- return -errno;
- }
+ if (pid < 0)
+ return log_error_errno(errno, "Failed to fork: %m");
if (pid == 0) {
const char **args;
@@ -6004,7 +6064,7 @@ static int run_editor(char **paths) {
* failing.
*/
if (errno != ENOENT) {
- log_error("Failed to execute %s: %m", editor);
+ log_error_errno(errno, "Failed to execute %s: %m", editor);
_exit(EXIT_FAILURE);
}
}
@@ -6017,14 +6077,13 @@ static int run_editor(char **paths) {
if (r < 0)
return log_error_errno(r, "Failed to wait for child: %m");
- return r;
+ return 0;
}
static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) {
_cleanup_free_ char *user_home = NULL;
_cleanup_free_ char *user_runtime = NULL;
_cleanup_lookup_paths_free_ LookupPaths lp = {};
- bool avoid_bus_cache;
char **name;
int r;
@@ -6035,13 +6094,11 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) {
if (r < 0)
return r;
- avoid_bus_cache = !bus || avoid_bus();
-
STRV_FOREACH(name, names) {
_cleanup_free_ char *path = NULL;
char *new_path, *tmp_path;
- r = unit_find_paths(bus, *name, avoid_bus_cache, &lp, &path, NULL);
+ r = unit_find_paths(bus, *name, &lp, &path, NULL);
if (r < 0)
return r;
else if (r == 0)
@@ -6067,25 +6124,28 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) {
return 0;
}
-static int edit(sd_bus *bus, char **args) {
+static int edit(int argc, char *argv[], void *userdata) {
_cleanup_strv_free_ char **names = NULL;
_cleanup_strv_free_ char **paths = NULL;
char **original, **tmp;
+ sd_bus *bus;
int r;
- assert(args);
-
if (!on_tty()) {
- log_error("Cannot edit units if not on a tty");
+ log_error("Cannot edit units if not on a tty.");
return -EINVAL;
}
if (arg_transport != BUS_TRANSPORT_LOCAL) {
- log_error("Cannot remotely edit units");
+ log_error("Cannot edit units remotely.");
return -EINVAL;
}
- r = expand_names(bus, args + 1, NULL, &names);
+ r = acquire_bus(BUS_MANAGER, &bus);
+ if (r < 0)
+ return r;
+
+ r = expand_names(bus, strv_skip(argv, 1), NULL, &names);
if (r < 0)
return log_error_errno(r, "Failed to expand names: %m");
@@ -6101,13 +6161,14 @@ static int edit(sd_bus *bus, char **args) {
goto end;
STRV_FOREACH_PAIR(original, tmp, paths) {
- /* If the temporary file is empty we ignore it.
- * It's useful if the user wants to cancel its modification
+ /* If the temporary file is empty we ignore it. It's
+ * useful if the user wants to cancel its modification
*/
if (null_or_empty_path(*tmp)) {
- log_warning("Editing \"%s\" canceled: temporary file is empty", *original);
+ log_warning("Editing \"%s\" canceled: temporary file is empty.", *original);
continue;
}
+
r = rename(*tmp, *original);
if (r < 0) {
r = log_error_errno(errno, "Failed to rename \"%s\" to \"%s\": %m", *tmp, *original);
@@ -6115,12 +6176,14 @@ static int edit(sd_bus *bus, char **args) {
}
}
- if (!arg_no_reload && bus && !avoid_bus())
- r = daemon_reload(bus, args);
+ r = 0;
+
+ if (!arg_no_reload && !install_client_side())
+ r = daemon_reload(argc, argv, userdata);
end:
STRV_FOREACH_PAIR(original, tmp, paths)
- unlink_noerrno(*tmp);
+ (void) unlink(*tmp);
return r;
}
@@ -6230,9 +6293,6 @@ static void systemctl_help(void) {
"Job Commands:\n"
" list-jobs [PATTERN...] List jobs\n"
" cancel [JOB...] Cancel all, one, or more jobs\n\n"
- "Snapshot Commands:\n"
- " snapshot [NAME] Create a snapshot\n"
- " delete NAME... Remove one or more snapshots\n\n"
"Environment Commands:\n"
" show-environment Dump environment\n"
" set-environment NAME=VALUE... Set one or more environment variables\n"
@@ -6314,15 +6374,85 @@ static void runlevel_help(void) {
static void help_types(void) {
int i;
- const char *t;
if (!arg_no_legend)
puts("Available unit types:");
- for (i = 0; i < _UNIT_TYPE_MAX; i++) {
- t = unit_type_to_string(i);
- if (t)
- puts(t);
- }
+ for (i = 0; i < _UNIT_TYPE_MAX; i++)
+ puts(unit_type_to_string(i));
+}
+
+static void help_states(void) {
+ int i;
+
+ if (!arg_no_legend)
+ puts("Available unit load states:");
+ for (i = 0; i < _UNIT_LOAD_STATE_MAX; i++)
+ puts(unit_load_state_to_string(i));
+
+ if (!arg_no_legend)
+ puts("\nAvailable unit active states:");
+ for (i = 0; i < _UNIT_ACTIVE_STATE_MAX; i++)
+ puts(unit_active_state_to_string(i));
+
+ if (!arg_no_legend)
+ puts("\nAvailable automount unit substates:");
+ for (i = 0; i < _AUTOMOUNT_STATE_MAX; i++)
+ puts(automount_state_to_string(i));
+
+ if (!arg_no_legend)
+ puts("\nAvailable busname unit substates:");
+ for (i = 0; i < _BUSNAME_STATE_MAX; i++)
+ puts(busname_state_to_string(i));
+
+ if (!arg_no_legend)
+ puts("\nAvailable device unit substates:");
+ for (i = 0; i < _DEVICE_STATE_MAX; i++)
+ puts(device_state_to_string(i));
+
+ if (!arg_no_legend)
+ puts("\nAvailable mount unit substates:");
+ for (i = 0; i < _MOUNT_STATE_MAX; i++)
+ puts(mount_state_to_string(i));
+
+ if (!arg_no_legend)
+ puts("\nAvailable path unit substates:");
+ for (i = 0; i < _PATH_STATE_MAX; i++)
+ puts(path_state_to_string(i));
+
+ if (!arg_no_legend)
+ puts("\nAvailable scope unit substates:");
+ for (i = 0; i < _SCOPE_STATE_MAX; i++)
+ puts(scope_state_to_string(i));
+
+ if (!arg_no_legend)
+ puts("\nAvailable service unit substates:");
+ for (i = 0; i < _SERVICE_STATE_MAX; i++)
+ puts(service_state_to_string(i));
+
+ if (!arg_no_legend)
+ puts("\nAvailable slice unit substates:");
+ for (i = 0; i < _SLICE_STATE_MAX; i++)
+ puts(slice_state_to_string(i));
+
+ if (!arg_no_legend)
+ puts("\nAvailable socket unit substates:");
+ for (i = 0; i < _SOCKET_STATE_MAX; i++)
+ puts(socket_state_to_string(i));
+
+ if (!arg_no_legend)
+ puts("\nAvailable swap unit substates:");
+ for (i = 0; i < _SWAP_STATE_MAX; i++)
+ puts(swap_state_to_string(i));
+
+ if (!arg_no_legend)
+ puts("\nAvailable target unit substates:");
+ for (i = 0; i < _TARGET_STATE_MAX; i++)
+ puts(target_state_to_string(i));
+
+ if (!arg_no_legend)
+ puts("\nAvailable timer unit substates:");
+ for (i = 0; i < _TIMER_STATE_MAX; i++)
+ puts(timer_state_to_string(i));
}
static int systemctl_parse_argv(int argc, char *argv[]) {
@@ -6405,7 +6535,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
{}
};
- int c;
+ const char *p;
+ int c, r;
assert(argc >= 0);
assert(argv);
@@ -6422,20 +6553,24 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case 't': {
- const char *word, *state;
- size_t size;
+ if (isempty(optarg)) {
+ log_error("--type requires arguments.");
+ return -EINVAL;
+ }
+
+ p = optarg;
+ for(;;) {
+ _cleanup_free_ char *type = NULL;
- FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) {
- _cleanup_free_ char *type;
+ r = extract_first_word(&p, &type, ",", 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse type: %s", optarg);
- type = strndup(word, size);
- if (!type)
- return -ENOMEM;
+ if (r == 0)
+ break;
if (streq(type, "help")) {
help_types();
@@ -6443,7 +6578,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
}
if (unit_type_from_string(type) >= 0) {
- if (strv_push(&arg_types, type))
+ if (strv_push(&arg_types, type) < 0)
return log_oom();
type = NULL;
continue;
@@ -6453,7 +6588,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
* load states, but let's support this
* in --types= too for compatibility
* with old versions */
- if (unit_load_state_from_string(optarg) >= 0) {
+ if (unit_load_state_from_string(type) >= 0) {
if (strv_push(&arg_states, type) < 0)
return log_oom();
type = NULL;
@@ -6476,18 +6611,21 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
if (!arg_properties)
return log_oom();
} else {
- const char *word, *state;
- size_t size;
+ p = optarg;
+ for(;;) {
+ _cleanup_free_ char *prop = NULL;
- FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) {
- char *prop;
+ r = extract_first_word(&p, &prop, ",", 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse property: %s", optarg);
- prop = strndup(word, size);
- if (!prop)
- return log_oom();
+ if (r == 0)
+ break;
- if (strv_consume(&arg_properties, prop) < 0)
+ if (strv_push(&arg_properties, prop) < 0)
return log_oom();
+
+ prop = NULL;
}
}
@@ -6564,7 +6702,9 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
break;
case ARG_ROOT:
- arg_root = optarg;
+ r = parse_path_argument_and_warn(optarg, true, &arg_root);
+ if (r < 0)
+ return r;
break;
case 'l':
@@ -6598,7 +6738,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
break;
case 's':
- if ((arg_signal = signal_from_string_try_harder(optarg)) < 0) {
+ arg_signal = signal_from_string_try_harder(optarg);
+ if (arg_signal < 0) {
log_error("Failed to parse signal string %s.", optarg);
return -EINVAL;
}
@@ -6650,18 +6791,31 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
break;
case ARG_STATE: {
- const char *word, *state;
- size_t size;
+ if (isempty(optarg)) {
+ log_error("--signal requires arguments.");
+ return -EINVAL;
+ }
- FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) {
- char *s;
+ p = optarg;
+ for(;;) {
+ _cleanup_free_ char *s = NULL;
- s = strndup(word, size);
- if (!s)
- return log_oom();
+ r = extract_first_word(&p, &s, ",", 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse signal: %s", optarg);
- if (strv_consume(&arg_states, s) < 0)
+ if (r == 0)
+ break;
+
+ if (streq(s, "help")) {
+ help_states();
+ return 0;
+ }
+
+ if (strv_push(&arg_states, s) < 0)
return log_oom();
+
+ s = NULL;
}
break;
}
@@ -6800,7 +6954,7 @@ static int halt_parse_argv(int argc, char *argv[]) {
return 1;
}
-static int parse_time_spec(const char *t, usec_t *_u) {
+static int parse_shutdown_time_spec(const char *t, usec_t *_u) {
assert(t);
assert(_u);
@@ -6866,12 +7020,13 @@ static int shutdown_parse_argv(int argc, char *argv[]) {
{}
};
+ char **wall = NULL;
int c, r;
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "HPrhkKt:afFc", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "HPrhkKtafFc", options, NULL)) >= 0)
switch (c) {
case ARG_HELP:
@@ -6929,7 +7084,7 @@ static int shutdown_parse_argv(int argc, char *argv[]) {
}
if (argc > optind && arg_action != ACTION_CANCEL_SHUTDOWN) {
- r = parse_time_spec(argv[optind], &arg_when);
+ r = parse_shutdown_time_spec(argv[optind], &arg_when);
if (r < 0) {
log_error("Failed to parse time specification: %s", argv[optind]);
return r;
@@ -6939,10 +7094,16 @@ static int shutdown_parse_argv(int argc, char *argv[]) {
if (argc > optind && arg_action == ACTION_CANCEL_SHUTDOWN)
/* No time argument for shutdown cancel */
- arg_wall = argv + optind;
+ wall = argv + optind;
else if (argc > optind + 1)
/* We skip the time argument */
- arg_wall = argv + optind + 1;
+ wall = argv + optind + 1;
+
+ if (wall) {
+ arg_wall = strv_copy(wall);
+ if (!arg_wall)
+ return log_oom();
+ }
optind = argc;
@@ -7006,8 +7167,7 @@ static int telinit_parse_argv(int argc, char *argv[]) {
}
if (optind >= argc) {
- log_error("%s: required argument missing.",
- program_invocation_short_name);
+ log_error("%s: required argument missing.", program_invocation_short_name);
return -EINVAL;
}
@@ -7142,7 +7302,7 @@ _pure_ static int action_to_runlevel(void) {
}
static int talk_initctl(void) {
-
+#ifdef HAVE_SYSV_COMPAT
struct init_request request = {
.magic = INIT_MAGIC,
.sleeptime = 0,
@@ -7164,8 +7324,7 @@ static int talk_initctl(void) {
if (errno == ENOENT)
return 0;
- log_error_errno(errno, "Failed to open "INIT_FIFO": %m");
- return -errno;
+ return log_error_errno(errno, "Failed to open "INIT_FIFO": %m");
}
r = loop_write(fd, &request, sizeof(request), false);
@@ -7173,177 +7332,89 @@ static int talk_initctl(void) {
return log_error_errno(r, "Failed to write to "INIT_FIFO": %m");
return 1;
+#else
+ return 0;
+#endif
}
-static int systemctl_main(sd_bus *bus, int argc, char *argv[], int bus_error) {
-
- static const struct {
- const char* verb;
- const enum {
- MORE,
- LESS,
- EQUAL
- } argc_cmp;
- const int argc;
- int (* const dispatch)(sd_bus *bus, char **args);
- const enum {
- NOBUS = 1,
- FORCE,
- } bus;
- } verbs[] = {
- { "list-units", MORE, 0, list_units },
- { "list-unit-files", MORE, 1, list_unit_files, NOBUS },
- { "list-sockets", MORE, 1, list_sockets },
- { "list-timers", MORE, 1, list_timers },
- { "list-jobs", MORE, 1, list_jobs },
- { "list-machines", MORE, 1, list_machines },
- { "clear-jobs", EQUAL, 1, daemon_reload },
- { "cancel", MORE, 2, cancel_job },
- { "start", MORE, 2, start_unit },
- { "stop", MORE, 2, start_unit },
- { "condstop", MORE, 2, start_unit }, /* For compatibility with ALTLinux */
- { "reload", MORE, 2, start_unit },
- { "restart", MORE, 2, start_unit },
- { "try-restart", MORE, 2, start_unit },
- { "reload-or-restart", MORE, 2, start_unit },
- { "reload-or-try-restart", MORE, 2, start_unit },
- { "force-reload", MORE, 2, start_unit }, /* For compatibility with SysV */
- { "condreload", MORE, 2, start_unit }, /* For compatibility with ALTLinux */
- { "condrestart", MORE, 2, start_unit }, /* For compatibility with RH */
- { "isolate", EQUAL, 2, start_unit },
- { "kill", MORE, 2, kill_unit },
- { "is-active", MORE, 2, check_unit_active },
- { "check", MORE, 2, check_unit_active },
- { "is-failed", MORE, 2, check_unit_failed },
- { "show", MORE, 1, show },
- { "cat", MORE, 2, cat, NOBUS },
- { "status", MORE, 1, show },
- { "help", MORE, 2, show },
- { "snapshot", LESS, 2, snapshot },
- { "delete", MORE, 2, delete_snapshot },
- { "daemon-reload", EQUAL, 1, daemon_reload },
- { "daemon-reexec", EQUAL, 1, daemon_reload },
- { "show-environment", EQUAL, 1, show_environment },
- { "set-environment", MORE, 2, set_environment },
- { "unset-environment", MORE, 2, set_environment },
- { "import-environment", MORE, 1, import_environment},
- { "halt", EQUAL, 1, start_special, FORCE },
- { "poweroff", EQUAL, 1, start_special, FORCE },
- { "reboot", MORE, 1, start_special, FORCE },
- { "kexec", EQUAL, 1, start_special },
- { "suspend", EQUAL, 1, start_special },
- { "hibernate", EQUAL, 1, start_special },
- { "hybrid-sleep", EQUAL, 1, start_special },
- { "default", EQUAL, 1, start_special },
- { "rescue", EQUAL, 1, start_special },
- { "emergency", EQUAL, 1, start_special },
- { "exit", LESS, 2, start_special },
- { "reset-failed", MORE, 1, reset_failed },
- { "enable", MORE, 2, enable_unit, NOBUS },
- { "disable", MORE, 2, enable_unit, NOBUS },
- { "is-enabled", MORE, 2, unit_is_enabled, NOBUS },
- { "reenable", MORE, 2, enable_unit, NOBUS },
- { "preset", MORE, 2, enable_unit, NOBUS },
- { "preset-all", EQUAL, 1, preset_all, NOBUS },
- { "mask", MORE, 2, enable_unit, NOBUS },
- { "unmask", MORE, 2, enable_unit, NOBUS },
- { "link", MORE, 2, enable_unit, NOBUS },
- { "switch-root", MORE, 2, switch_root },
- { "list-dependencies", LESS, 2, list_dependencies },
- { "set-default", EQUAL, 2, set_default, NOBUS },
- { "get-default", EQUAL, 1, get_default, NOBUS },
- { "set-property", MORE, 3, set_property },
- { "is-system-running", EQUAL, 1, is_system_running },
- { "add-wants", MORE, 3, add_dependency, NOBUS },
- { "add-requires", MORE, 3, add_dependency, NOBUS },
- { "edit", MORE, 2, edit, NOBUS },
+static int systemctl_main(int argc, char *argv[]) {
+
+ static const Verb verbs[] = {
+ { "list-units", VERB_ANY, VERB_ANY, VERB_DEFAULT, list_units },
+ { "list-unit-files", VERB_ANY, VERB_ANY, 0, list_unit_files },
+ { "list-sockets", VERB_ANY, VERB_ANY, 0, list_sockets },
+ { "list-timers", VERB_ANY, VERB_ANY, 0, list_timers },
+ { "list-jobs", VERB_ANY, VERB_ANY, 0, list_jobs },
+ { "list-machines", VERB_ANY, VERB_ANY, 0, list_machines },
+ { "clear-jobs", VERB_ANY, 1, 0, daemon_reload },
+ { "cancel", VERB_ANY, VERB_ANY, 0, cancel_job },
+ { "start", 2, VERB_ANY, 0, start_unit },
+ { "stop", 2, VERB_ANY, 0, start_unit },
+ { "condstop", 2, VERB_ANY, 0, start_unit }, /* For compatibility with ALTLinux */
+ { "reload", 2, VERB_ANY, 0, start_unit },
+ { "restart", 2, VERB_ANY, 0, start_unit },
+ { "try-restart", 2, VERB_ANY, 0, start_unit },
+ { "reload-or-restart", 2, VERB_ANY, 0, start_unit },
+ { "reload-or-try-restart", 2, VERB_ANY, 0, start_unit },
+ { "force-reload", 2, VERB_ANY, 0, start_unit }, /* For compatibility with SysV */
+ { "condreload", 2, VERB_ANY, 0, start_unit }, /* For compatibility with ALTLinux */
+ { "condrestart", 2, VERB_ANY, 0, start_unit }, /* For compatibility with RH */
+ { "isolate", 2, 2, 0, start_unit },
+ { "kill", 2, VERB_ANY, 0, kill_unit },
+ { "is-active", 2, VERB_ANY, 0, check_unit_active },
+ { "check", 2, VERB_ANY, 0, check_unit_active },
+ { "is-failed", 2, VERB_ANY, 0, check_unit_failed },
+ { "show", VERB_ANY, VERB_ANY, 0, show },
+ { "cat", 2, VERB_ANY, 0, cat },
+ { "status", VERB_ANY, VERB_ANY, 0, show },
+ { "help", VERB_ANY, VERB_ANY, 0, show },
+ { "daemon-reload", VERB_ANY, 1, 0, daemon_reload },
+ { "daemon-reexec", VERB_ANY, 1, 0, daemon_reload },
+ { "show-environment", VERB_ANY, 1, 0, show_environment },
+ { "set-environment", 2, VERB_ANY, 0, set_environment },
+ { "unset-environment", 2, VERB_ANY, 0, set_environment },
+ { "import-environment", VERB_ANY, VERB_ANY, 0, import_environment},
+ { "halt", VERB_ANY, 1, 0, start_special },
+ { "poweroff", VERB_ANY, 1, 0, start_special },
+ { "reboot", VERB_ANY, 2, 0, start_special },
+ { "kexec", VERB_ANY, 1, 0, start_special },
+ { "suspend", VERB_ANY, 1, 0, start_special },
+ { "hibernate", VERB_ANY, 1, 0, start_special },
+ { "hybrid-sleep", VERB_ANY, 1, 0, start_special },
+ { "default", VERB_ANY, 1, 0, start_special },
+ { "rescue", VERB_ANY, 1, 0, start_special },
+ { "emergency", VERB_ANY, 1, 0, start_special },
+ { "exit", VERB_ANY, 2, 0, start_special },
+ { "reset-failed", VERB_ANY, VERB_ANY, 0, reset_failed },
+ { "enable", 2, VERB_ANY, 0, enable_unit },
+ { "disable", 2, VERB_ANY, 0, enable_unit },
+ { "is-enabled", 2, VERB_ANY, 0, unit_is_enabled },
+ { "reenable", 2, VERB_ANY, 0, enable_unit },
+ { "preset", 2, VERB_ANY, 0, enable_unit },
+ { "preset-all", VERB_ANY, 1, 0, preset_all },
+ { "mask", 2, VERB_ANY, 0, enable_unit },
+ { "unmask", 2, VERB_ANY, 0, enable_unit },
+ { "link", 2, VERB_ANY, 0, enable_unit },
+ { "switch-root", 2, VERB_ANY, 0, switch_root },
+ { "list-dependencies", VERB_ANY, 2, 0, list_dependencies },
+ { "set-default", 2, 2, 0, set_default },
+ { "get-default", VERB_ANY, 1, 0, get_default, },
+ { "set-property", 3, VERB_ANY, 0, set_property },
+ { "is-system-running", VERB_ANY, 1, 0, is_system_running },
+ { "add-wants", 3, VERB_ANY, 0, add_dependency },
+ { "add-requires", 3, VERB_ANY, 0, add_dependency },
+ { "edit", 2, VERB_ANY, 0, edit },
{}
- }, *verb = verbs;
-
- int left;
-
- assert(argc >= 0);
- assert(argv);
-
- left = argc - optind;
-
- /* Special rule: no arguments (left == 0) means "list-units" */
- if (left > 0) {
- if (streq(argv[optind], "help") && !argv[optind+1]) {
- log_error("This command expects one or more "
- "unit names. Did you mean --help?");
- return -EINVAL;
- }
-
- for (; verb->verb; verb++)
- if (streq(argv[optind], verb->verb))
- goto found;
-
- log_error("Unknown operation '%s'.", argv[optind]);
- return -EINVAL;
- }
-found:
-
- switch (verb->argc_cmp) {
-
- case EQUAL:
- if (left != verb->argc) {
- log_error("Invalid number of arguments.");
- return -EINVAL;
- }
-
- break;
-
- case MORE:
- if (left < verb->argc) {
- log_error("Too few arguments.");
- return -EINVAL;
- }
-
- break;
-
- case LESS:
- if (left > verb->argc) {
- log_error("Too many arguments.");
- return -EINVAL;
- }
-
- break;
-
- default:
- assert_not_reached("Unknown comparison operator.");
- }
-
- /* Require a bus connection for all operations but
- * enable/disable */
- if (verb->bus == NOBUS) {
- if (!bus && !avoid_bus()) {
- log_error_errno(bus_error, "Failed to get D-Bus connection: %m");
- return -EIO;
- }
-
- } else {
- if (running_in_chroot() > 0) {
- log_info("Running in chroot, ignoring request.");
- return 0;
- }
-
- if ((verb->bus != FORCE || arg_force <= 0) && !bus) {
- log_error_errno(bus_error, "Failed to get D-Bus connection: %m");
- return -EIO;
- }
- }
+ };
- return verb->dispatch(bus, argv + optind);
+ return dispatch_verb(argc, argv, verbs, NULL);
}
-static int reload_with_fallback(sd_bus *bus) {
+static int reload_with_fallback(void) {
- if (bus) {
- /* First, try systemd via D-Bus. */
- if (daemon_reload(bus, NULL) >= 0)
- return 0;
- }
+ /* First, try systemd via D-Bus. */
+ if (daemon_reload(0, NULL, NULL) >= 0)
+ return 0;
/* Nothing else worked, so let's try signals */
assert(arg_action == ACTION_RELOAD || arg_action == ACTION_REEXEC);
@@ -7354,25 +7425,19 @@ static int reload_with_fallback(sd_bus *bus) {
return 0;
}
-static int start_with_fallback(sd_bus *bus) {
+static int start_with_fallback(void) {
- if (bus) {
- /* First, try systemd via D-Bus. */
- if (start_unit(bus, NULL) >= 0)
- goto done;
- }
+ /* First, try systemd via D-Bus. */
+ if (start_unit(0, NULL, NULL) >= 0)
+ return 0;
/* Nothing else worked, so let's try
* /dev/initctl */
if (talk_initctl() > 0)
- goto done;
+ return 0;
log_error("Failed to talk to init daemon.");
return -EIO;
-
-done:
- warn_wall(arg_action);
- return 0;
}
static int halt_now(enum action a) {
@@ -7380,22 +7445,22 @@ static int halt_now(enum action a) {
/* The kernel will automaticall flush ATA disks and suchlike
* on reboot(), but the file systems need to be synce'd
* explicitly in advance. */
- sync();
+ (void) sync();
/* Make sure C-A-D is handled by the kernel from this point
* on... */
- reboot(RB_ENABLE_CAD);
+ (void) reboot(RB_ENABLE_CAD);
switch (a) {
case ACTION_HALT:
log_info("Halting.");
- reboot(RB_HALT_SYSTEM);
+ (void) reboot(RB_HALT_SYSTEM);
return -errno;
case ACTION_POWEROFF:
log_info("Powering off.");
- reboot(RB_POWER_OFF);
+ (void) reboot(RB_POWER_OFF);
return -errno;
case ACTION_KEXEC:
@@ -7404,12 +7469,11 @@ static int halt_now(enum action a) {
if (read_one_line_file(REBOOT_PARAM_FILE, &param) >= 0) {
log_info("Rebooting with argument '%s'.", param);
- syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
- LINUX_REBOOT_CMD_RESTART2, param);
+ (void) syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, param);
}
log_info("Rebooting.");
- reboot(RB_AUTOBOOT);
+ (void) reboot(RB_AUTOBOOT);
return -errno;
}
@@ -7418,17 +7482,77 @@ static int halt_now(enum action a) {
}
}
-static int halt_main(sd_bus *bus) {
+static int logind_schedule_shutdown(void) {
+
+#ifdef HAVE_LOGIND
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ char date[FORMAT_TIMESTAMP_MAX];
+ const char *action;
+ sd_bus *bus;
int r;
- r = check_inhibitors(bus, arg_action);
+ (void) logind_set_wall_message();
+
+ r = acquire_bus(BUS_FULL, &bus);
if (r < 0)
return r;
+ switch (arg_action) {
+ case ACTION_HALT:
+ action = "halt";
+ break;
+ case ACTION_POWEROFF:
+ action = "poweroff";
+ break;
+ case ACTION_KEXEC:
+ action = "kexec";
+ break;
+ case ACTION_EXIT:
+ action = "exit";
+ break;
+ case ACTION_REBOOT:
+ default:
+ action = "reboot";
+ break;
+ }
+
+ if (arg_dry)
+ action = strjoina("dry-", action);
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "ScheduleShutdown",
+ &error,
+ NULL,
+ "st",
+ action,
+ arg_when);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to call ScheduleShutdown in logind, proceeding with immediate shutdown: %s", bus_error_message(&error, r));
+
+ log_info("Shutdown scheduled for %s, use 'shutdown -c' to cancel.", format_timestamp(date, sizeof(date), arg_when));
+ return 0;
+#else
+ log_error("Cannot schedule shutdown without logind support, proceeding with immediate shutdown.");
+ return -ENOSYS;
+#endif
+}
+
+static int halt_main(void) {
+ int r;
+
+ r = logind_check_inhibitors(arg_action);
+ if (r < 0)
+ return r;
+
+ if (arg_when > 0)
+ return logind_schedule_shutdown();
+
if (geteuid() != 0) {
- if (arg_when > 0 ||
- arg_dry ||
- arg_force > 0) {
+ if (arg_dry || arg_force > 0) {
log_error("Must be root.");
return -EPERM;
}
@@ -7436,101 +7560,21 @@ static int halt_main(sd_bus *bus) {
/* Try logind if we are a normal user and no special
* mode applies. Maybe PolicyKit allows us to shutdown
* the machine. */
- if (IN_SET(arg_action,
- ACTION_POWEROFF,
- ACTION_REBOOT)) {
- r = reboot_with_logind(bus, arg_action);
+ if (IN_SET(arg_action, ACTION_POWEROFF, ACTION_REBOOT)) {
+ r = logind_reboot(arg_action);
if (r >= 0)
return r;
if (IN_SET(r, -EOPNOTSUPP, -EINPROGRESS))
- /* requested operation is not supported or already in progress */
+ /* requested operation is not
+ * supported on the local system or
+ * already in progress */
return r;
/* on all other errors, try low-level operation */
}
}
- if (arg_when > 0) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_flush_close_unref_ sd_bus *b = NULL;
- _cleanup_free_ char *m = NULL;
- const char *action;
-
- assert(geteuid() == 0);
-
- 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 = sd_bus_call_method(
- b,
- "org.freedesktop.login1",
- "/org/freedesktop/login1",
- "org.freedesktop.login1.Manager",
- "SetWallMessage",
- &error,
- NULL,
- "sb",
- m,
- !arg_no_wall);
-
- if (r < 0) {
- log_warning_errno(r, "Failed to set wall message, ignoring: %s",
- bus_error_message(&error, r));
- sd_bus_error_free(&error);
- }
-
- switch (arg_action) {
- case ACTION_HALT:
- action = "halt";
- break;
- case ACTION_POWEROFF:
- action = "poweroff";
- break;
- case ACTION_KEXEC:
- action = "kexec";
- break;
- default:
- action = "reboot";
- break;
- }
-
- if (arg_dry)
- action = strjoina("dry-", action);
-
- r = sd_bus_call_method(
- b,
- "org.freedesktop.login1",
- "/org/freedesktop/login1",
- "org.freedesktop.login1.Manager",
- "ScheduleShutdown",
- &error,
- NULL,
- "st",
- action,
- arg_when);
- if (r < 0)
- 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];
-
- log_info("Shutdown scheduled for %s, use 'shutdown -c' to cancel.",
- format_timestamp(date, sizeof(date), arg_when));
- return 0;
- }
- }
-
if (!arg_dry && !arg_force)
- return start_with_fallback(bus);
+ return start_with_fallback();
assert(geteuid() == 0);
@@ -7548,9 +7592,7 @@ static int halt_main(sd_bus *bus) {
return 0;
r = halt_now(arg_action);
- log_error_errno(r, "Failed to reboot: %m");
-
- return r;
+ return log_error_errno(r, "Failed to reboot: %m");
}
static int runlevel_main(void) {
@@ -7569,8 +7611,37 @@ static int runlevel_main(void) {
return 0;
}
+static int logind_cancel_shutdown(void) {
+#ifdef HAVE_LOGIND
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ sd_bus *bus;
+ int r;
+
+ r = acquire_bus(BUS_FULL, &bus);
+ if (r < 0)
+ return r;
+
+ (void) logind_set_wall_message();
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "CancelScheduledShutdown",
+ &error,
+ NULL, NULL);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to talk to logind, shutdown hasn't been cancelled: %s", bus_error_message(&error, r));
+
+ return 0;
+#else
+ log_error("Not compiled with logind support, cannot cancel scheduled shutdowns.");
+ return -ENOSYS;
+#endif
+}
+
int main(int argc, char*argv[]) {
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
int r;
setlocale(LC_ALL, "");
@@ -7586,39 +7657,26 @@ int main(int argc, char*argv[]) {
if (r <= 0)
goto finish;
- /* /sbin/runlevel doesn't need to communicate via D-Bus, so
- * let's shortcut this */
- if (arg_action == ACTION_RUNLEVEL) {
- r = runlevel_main();
- goto finish;
- }
-
if (running_in_chroot() > 0 && arg_action != ACTION_SYSTEMCTL) {
log_info("Running in chroot, ignoring request.");
r = 0;
goto finish;
}
- 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 */
switch (arg_action) {
case ACTION_SYSTEMCTL:
- r = systemctl_main(bus, argc, argv, r);
+ r = systemctl_main(argc, argv);
break;
case ACTION_HALT:
case ACTION_POWEROFF:
case ACTION_REBOOT:
case ACTION_KEXEC:
- r = halt_main(bus);
+ r = halt_main();
break;
case ACTION_RUNLEVEL2:
@@ -7628,69 +7686,22 @@ int main(int argc, char*argv[]) {
case ACTION_RESCUE:
case ACTION_EMERGENCY:
case ACTION_DEFAULT:
- r = start_with_fallback(bus);
+ r = start_with_fallback();
break;
case ACTION_RELOAD:
case ACTION_REEXEC:
- r = reload_with_fallback(bus);
+ r = reload_with_fallback();
break;
- case ACTION_CANCEL_SHUTDOWN: {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_flush_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) {
- r = log_oom();
- goto finish;
- }
- }
-
- r = sd_bus_call_method(
- b,
- "org.freedesktop.login1",
- "/org/freedesktop/login1",
- "org.freedesktop.login1.Manager",
- "SetWallMessage",
- &error,
- NULL,
- "sb",
- m,
- !arg_no_wall);
-
- if (r < 0) {
- log_warning_errno(r, "Failed to set wall message, ignoring: %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 logind, shutdown hasn't been cancelled: %s",
- bus_error_message(&error, r));
+ case ACTION_CANCEL_SHUTDOWN:
+ r = logind_cancel_shutdown();
break;
- }
case ACTION_RUNLEVEL:
+ r = runlevel_main();
+ break;
+
case _ACTION_INVALID:
default:
assert_not_reached("Unknown action");
@@ -7705,7 +7716,12 @@ finish:
strv_free(arg_states);
strv_free(arg_properties);
- sd_bus_default_flush_close();
+ strv_free(arg_wall);
+ free(arg_root);
+
+ release_busses();
+
+ /* Note that we return r here, not EXIT_SUCCESS, so that we can implement the LSB-like return codes */
return r < 0 ? EXIT_FAILURE : r;
}
diff --git a/src/systemd/_sd-common.h b/src/systemd/_sd-common.h
index 896a027eb5..6b5e6c50f9 100644
--- a/src/systemd/_sd-common.h
+++ b/src/systemd/_sd-common.h
@@ -57,10 +57,10 @@
# ifdef __cplusplus
# define _SD_BEGIN_DECLARATIONS \
extern "C" { \
- struct __useless_struct_to_allow_trailing_semicolon__
+ struct _sd_useless_struct_to_allow_trailing_semicolon_
# else
# define _SD_BEGIN_DECLARATIONS \
- struct __useless_struct_to_allow_trailing_semicolon__
+ struct _sd_useless_struct_to_allow_trailing_semicolon_
# endif
#endif
@@ -68,11 +68,18 @@
# ifdef __cplusplus
# define _SD_END_DECLARATIONS \
} \
- struct __useless_struct_to_allow_trailing_semicolon__
+ struct _sd_useless_cpp_struct_to_allow_trailing_semicolon_
# else
# define _SD_END_DECLARATIONS \
- struct __useless_struct_to_allow_trailing_semicolon__
+ struct _sd_useless_struct_to_allow_trailing_semicolon_
# endif
#endif
+#define _SD_DEFINE_POINTER_CLEANUP_FUNC(type, func) \
+ static inline void func##p(type **p) { \
+ if (*p) \
+ func(*p); \
+ } \
+ struct _sd_useless_struct_to_allow_trailing_semicolon_
+
#endif
diff --git a/src/systemd/sd-bus.h b/src/systemd/sd-bus.h
index 0883203ae7..ac156fe19f 100644
--- a/src/systemd/sd-bus.h
+++ b/src/systemd/sd-bus.h
@@ -27,8 +27,9 @@
#include <sys/types.h>
#include <sys/uio.h>
-#include "sd-id128.h"
#include "sd-event.h"
+#include "sd-id128.h"
+
#include "_sd-common.h"
_SD_BEGIN_DECLARATIONS;
@@ -420,7 +421,9 @@ int sd_bus_error_add_map(const sd_bus_error_map *map);
/* Label escaping */
int sd_bus_path_encode(const char *prefix, const char *external_id, char **ret_path);
+int sd_bus_path_encode_many(char **out, const char *path_template, ...);
int sd_bus_path_decode(const char *path, const char *prefix, char **ret_external_id);
+int sd_bus_path_decode_many(const char *path, const char *path_template, ...);
/* Tracking peers */
@@ -442,6 +445,14 @@ const char* sd_bus_track_contains(sd_bus_track *track, const char *names);
const char* sd_bus_track_first(sd_bus_track *track);
const char* sd_bus_track_next(sd_bus_track *track);
+/* Define helpers so that __attribute__((cleanup(sd_bus_unrefp))) and similar may be used. */
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_bus, sd_bus_unref);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_bus, sd_bus_flush_close_unref);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_bus_slot, sd_bus_slot_unref);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_bus_message, sd_bus_message_unref);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_bus_creds, sd_bus_creds_unref);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_bus_track, sd_bus_track_unref);
+
_SD_END_DECLARATIONS;
#endif
diff --git a/src/systemd/sd-daemon.h b/src/systemd/sd-daemon.h
index 861dc8f1f4..c26cd1be3a 100644
--- a/src/systemd/sd-daemon.h
+++ b/src/systemd/sd-daemon.h
@@ -22,8 +22,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
#include <inttypes.h>
+#include <sys/types.h>
#include "_sd-common.h"
@@ -76,6 +76,8 @@ _SD_BEGIN_DECLARATIONS;
*/
int sd_listen_fds(int unset_environment);
+int sd_listen_fds_with_names(int unset_environment, char ***names);
+
/*
Helper call for identifying a passed file descriptor. Returns 1 if
the file descriptor is a FIFO in the file system stored under the
diff --git a/src/systemd/sd-device.h b/src/systemd/sd-device.h
index 38cb2a1102..d3d4abd6ce 100644
--- a/src/systemd/sd-device.h
+++ b/src/systemd/sd-device.h
@@ -23,8 +23,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <inttypes.h>
#include <sys/types.h>
-#include <stdint.h>
#include "_sd-common.h"
@@ -94,6 +94,9 @@ int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator, const c
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_DEFINE_POINTER_CLEANUP_FUNC(sd_device, sd_device_unref);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_device_enumerator, sd_device_enumerator_unref);
+
_SD_END_DECLARATIONS;
#endif
diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h
index 4291fb7ebc..bbf220e689 100644
--- a/src/systemd/sd-dhcp-client.h
+++ b/src/systemd/sd-dhcp-client.h
@@ -22,11 +22,17 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <netinet/in.h>
+#include <inttypes.h>
#include <net/ethernet.h>
+#include <netinet/in.h>
+#include <sys/types.h>
-#include "sd-event.h"
#include "sd-dhcp-lease.h"
+#include "sd-event.h"
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
enum {
SD_DHCP_CLIENT_EVENT_STOP = 0,
@@ -36,6 +42,48 @@ enum {
SD_DHCP_CLIENT_EVENT_RENEW = 4,
};
+enum {
+ SD_DHCP_OPTION_PAD = 0,
+ SD_DHCP_OPTION_SUBNET_MASK = 1,
+ SD_DHCP_OPTION_TIME_OFFSET = 2,
+ SD_DHCP_OPTION_ROUTER = 3,
+ SD_DHCP_OPTION_DOMAIN_NAME_SERVER = 6,
+ SD_DHCP_OPTION_HOST_NAME = 12,
+ SD_DHCP_OPTION_BOOT_FILE_SIZE = 13,
+ SD_DHCP_OPTION_DOMAIN_NAME = 15,
+ SD_DHCP_OPTION_ROOT_PATH = 17,
+ SD_DHCP_OPTION_ENABLE_IP_FORWARDING = 19,
+ SD_DHCP_OPTION_ENABLE_IP_FORWARDING_NL = 20,
+ SD_DHCP_OPTION_POLICY_FILTER = 21,
+ SD_DHCP_OPTION_INTERFACE_MDR = 22,
+ SD_DHCP_OPTION_INTERFACE_TTL = 23,
+ SD_DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT = 24,
+ SD_DHCP_OPTION_INTERFACE_MTU = 26,
+ SD_DHCP_OPTION_BROADCAST = 28,
+ SD_DHCP_OPTION_STATIC_ROUTE = 33,
+ SD_DHCP_OPTION_NTP_SERVER = 42,
+ SD_DHCP_OPTION_VENDOR_SPECIFIC = 43,
+ SD_DHCP_OPTION_REQUESTED_IP_ADDRESS = 50,
+ SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME = 51,
+ SD_DHCP_OPTION_OVERLOAD = 52,
+ SD_DHCP_OPTION_MESSAGE_TYPE = 53,
+ SD_DHCP_OPTION_SERVER_IDENTIFIER = 54,
+ SD_DHCP_OPTION_PARAMETER_REQUEST_LIST = 55,
+ SD_DHCP_OPTION_ERROR_MESSAGE = 56,
+ SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE = 57,
+ SD_DHCP_OPTION_RENEWAL_T1_TIME = 58,
+ SD_DHCP_OPTION_REBINDING_T2_TIME = 59,
+ SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER = 60,
+ SD_DHCP_OPTION_CLIENT_IDENTIFIER = 61,
+ SD_DHCP_OPTION_FQDN = 81,
+ SD_DHCP_OPTION_NEW_POSIX_TIMEZONE = 100,
+ SD_DHCP_OPTION_NEW_TZDB_TIMEZONE = 101,
+ SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121,
+ SD_DHCP_OPTION_PRIVATE_BASE = 224,
+ SD_DHCP_OPTION_PRIVATE_LAST = 254,
+ SD_DHCP_OPTION_END = 255,
+};
+
typedef struct sd_dhcp_client sd_dhcp_client;
typedef void (*sd_dhcp_client_cb_t)(sd_dhcp_client *client, int event,
@@ -43,7 +91,6 @@ typedef void (*sd_dhcp_client_cb_t)(sd_dhcp_client *client, int event,
int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb,
void *userdata);
-
int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option);
int sd_dhcp_client_set_request_address(sd_dhcp_client *client,
const struct in_addr *last_address);
@@ -72,4 +119,8 @@ int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event, int pri
int sd_dhcp_client_detach_event(sd_dhcp_client *client);
sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_client, sd_dhcp_client_unref);
+
+_SD_END_DECLARATIONS;
+
#endif
diff --git a/src/systemd/sd-dhcp-lease.h b/src/systemd/sd-dhcp-lease.h
index ed5bceecdd..a0d24c211c 100644
--- a/src/systemd/sd-dhcp-lease.h
+++ b/src/systemd/sd-dhcp-lease.h
@@ -23,11 +23,17 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <netinet/in.h>
+#include <inttypes.h>
#include <net/ethernet.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
typedef struct sd_dhcp_lease sd_dhcp_lease;
-struct sd_dhcp_route;
+typedef struct sd_dhcp_route sd_dhcp_route;
sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease);
sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease);
@@ -47,9 +53,17 @@ int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu);
int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname);
int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname);
int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path);
-int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, struct sd_dhcp_route **routes);
+int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes);
int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len);
int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len);
int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **timezone);
+int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *destination);
+int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *length);
+int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *gateway);
+
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_lease, sd_dhcp_lease_unref);
+
+_SD_END_DECLARATIONS;
+
#endif
diff --git a/src/systemd/sd-dhcp-server.h b/src/systemd/sd-dhcp-server.h
index 4b0c7a1852..dbebb4f294 100644
--- a/src/systemd/sd-dhcp-server.h
+++ b/src/systemd/sd-dhcp-server.h
@@ -23,11 +23,15 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdbool.h>
+#include <inttypes.h>
#include <netinet/in.h>
#include "sd-event.h"
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
typedef struct sd_dhcp_server sd_dhcp_server;
int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex);
@@ -39,7 +43,7 @@ int sd_dhcp_server_attach_event(sd_dhcp_server *client, sd_event *event, int pri
int sd_dhcp_server_detach_event(sd_dhcp_server *client);
sd_event *sd_dhcp_server_get_event(sd_dhcp_server *client);
-bool sd_dhcp_server_is_running(sd_dhcp_server *server);
+int sd_dhcp_server_is_running(sd_dhcp_server *server);
int sd_dhcp_server_start(sd_dhcp_server *server);
int sd_dhcp_server_stop(sd_dhcp_server *server);
@@ -55,4 +59,8 @@ int sd_dhcp_server_set_default_lease_time(sd_dhcp_server *server, uint32_t t);
int sd_dhcp_server_forcerenew(sd_dhcp_server *server);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_server, sd_dhcp_server_unref);
+
+_SD_END_DECLARATIONS;
+
#endif
diff --git a/src/systemd/sd-dhcp6-client.h b/src/systemd/sd-dhcp6-client.h
index 90c35ef3f6..fa90f7a670 100644
--- a/src/systemd/sd-dhcp6-client.h
+++ b/src/systemd/sd-dhcp6-client.h
@@ -22,11 +22,16 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <inttypes.h>
#include <net/ethernet.h>
+#include <sys/types.h>
+#include "sd-dhcp6-lease.h"
#include "sd-event.h"
-#include "sd-dhcp6-lease.h"
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
enum {
SD_DHCP6_CLIENT_EVENT_STOP = 0,
@@ -36,6 +41,41 @@ enum {
SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST = 13,
};
+enum {
+ SD_DHCP6_OPTION_CLIENTID = 1,
+ SD_DHCP6_OPTION_SERVERID = 2,
+ SD_DHCP6_OPTION_IA_NA = 3,
+ SD_DHCP6_OPTION_IA_TA = 4,
+ SD_DHCP6_OPTION_IAADDR = 5,
+ SD_DHCP6_OPTION_ORO = 6,
+ SD_DHCP6_OPTION_PREFERENCE = 7,
+ SD_DHCP6_OPTION_ELAPSED_TIME = 8,
+ SD_DHCP6_OPTION_RELAY_MSG = 9,
+ /* option code 10 is unassigned */
+ SD_DHCP6_OPTION_AUTH = 11,
+ SD_DHCP6_OPTION_UNICAST = 12,
+ SD_DHCP6_OPTION_STATUS_CODE = 13,
+ SD_DHCP6_OPTION_RAPID_COMMIT = 14,
+ SD_DHCP6_OPTION_USER_CLASS = 15,
+ SD_DHCP6_OPTION_VENDOR_CLASS = 16,
+ SD_DHCP6_OPTION_VENDOR_OPTS = 17,
+ SD_DHCP6_OPTION_INTERFACE_ID = 18,
+ SD_DHCP6_OPTION_RECONF_MSG = 19,
+ SD_DHCP6_OPTION_RECONF_ACCEPT = 20,
+
+ SD_DHCP6_OPTION_DNS_SERVERS = 23, /* RFC 3646 */
+ SD_DHCP6_OPTION_DOMAIN_LIST = 24, /* RFC 3646 */
+
+ SD_DHCP6_OPTION_SNTP_SERVERS = 31, /* RFC 4075, deprecated */
+
+ /* option code 35 is unassigned */
+
+ SD_DHCP6_OPTION_NTP_SERVER = 56, /* RFC 5908 */
+
+ /* option codes 89-142 are unassigned */
+ /* option codes 144-65535 are unassigned */
+};
+
typedef struct sd_dhcp6_client sd_dhcp6_client;
typedef void (*sd_dhcp6_client_cb_t)(sd_dhcp6_client *client, int event,
@@ -44,14 +84,13 @@ int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
sd_dhcp6_client_cb_t cb, void *userdata);
int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index);
+int sd_dhcp6_client_set_local_address(sd_dhcp6_client *client, const struct in6_addr *local_address);
int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, const uint8_t *addr,
size_t addr_len, uint16_t arp_type);
int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *duid,
size_t duid_len);
-int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client,
- bool enabled);
-int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client,
- bool *enabled);
+int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, int enabled);
+int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client, int *enabled);
int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client,
uint16_t option);
@@ -59,6 +98,7 @@ int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret);
int sd_dhcp6_client_stop(sd_dhcp6_client *client);
int sd_dhcp6_client_start(sd_dhcp6_client *client);
+int sd_dhcp6_client_is_running(sd_dhcp6_client *client);
int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
int priority);
int sd_dhcp6_client_detach_event(sd_dhcp6_client *client);
@@ -67,4 +107,8 @@ sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client);
sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client);
int sd_dhcp6_client_new(sd_dhcp6_client **ret);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp6_client, sd_dhcp6_client_unref);
+
+_SD_END_DECLARATIONS;
+
#endif
diff --git a/src/systemd/sd-dhcp6-lease.h b/src/systemd/sd-dhcp6-lease.h
index dc3df3bbf7..3a62d3402d 100644
--- a/src/systemd/sd-dhcp6-lease.h
+++ b/src/systemd/sd-dhcp6-lease.h
@@ -23,8 +23,13 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <inttypes.h>
#include <netinet/in.h>
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
typedef struct sd_dhcp6_lease sd_dhcp6_lease;
void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease);
@@ -42,4 +47,8 @@ int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn);
sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease);
sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp6_lease, sd_dhcp6_lease_unref);
+
+_SD_END_DECLARATIONS;
+
#endif
diff --git a/src/systemd/sd-event.h b/src/systemd/sd-event.h
index 565de5495a..59cea09e59 100644
--- a/src/systemd/sd-event.h
+++ b/src/systemd/sd-event.h
@@ -22,11 +22,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
-#include <sys/signalfd.h>
-#include <sys/epoll.h>
#include <inttypes.h>
#include <signal.h>
+#include <sys/epoll.h>
+#include <sys/signalfd.h>
+#include <sys/types.h>
#include "_sd-common.h"
@@ -56,7 +56,8 @@ enum {
SD_EVENT_PENDING,
SD_EVENT_RUNNING,
SD_EVENT_EXITING,
- SD_EVENT_FINISHED
+ SD_EVENT_FINISHED,
+ SD_EVENT_PREPARING,
};
enum {
@@ -87,9 +88,9 @@ int sd_event_add_post(sd_event *e, sd_event_source **s, sd_event_handler_t callb
int sd_event_add_exit(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata);
int sd_event_prepare(sd_event *e);
-int sd_event_wait(sd_event *e, uint64_t timeout);
+int sd_event_wait(sd_event *e, uint64_t usec);
int sd_event_dispatch(sd_event *e);
-int sd_event_run(sd_event *e, uint64_t timeout);
+int sd_event_run(sd_event *e, uint64_t usec);
int sd_event_loop(sd_event *e);
int sd_event_exit(sd_event *e, int code);
@@ -130,6 +131,10 @@ int sd_event_source_get_time_clock(sd_event_source *s, clockid_t *clock);
int sd_event_source_get_signal(sd_event_source *s);
int sd_event_source_get_child_pid(sd_event_source *s, pid_t *pid);
+/* Define helpers so that __attribute__((cleanup(sd_event_unrefp))) and similar may be used. */
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event, sd_event_unref);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event_source, sd_event_source_unref);
+
_SD_END_DECLARATIONS;
#endif
diff --git a/src/systemd/sd-hwdb.h b/src/systemd/sd-hwdb.h
index 3c44b981d6..c0b8d94c9e 100644
--- a/src/systemd/sd-hwdb.h
+++ b/src/systemd/sd-hwdb.h
@@ -39,9 +39,13 @@ int sd_hwdb_get(sd_hwdb *hwdb, const char *modalias, const char *key, const char
int sd_hwdb_seek(sd_hwdb *hwdb, const char *modalias);
int sd_hwdb_enumerate(sd_hwdb *hwdb, const char **key, const char **value);
-/* the inverse condition avoids ambiguity of danling 'else' after the macro */
+/* the inverse condition avoids ambiguity of dangling 'else' after the macro */
#define SD_HWDB_FOREACH_PROPERTY(hwdb, modalias, key, value) \
if (sd_hwdb_seek(hwdb, modalias) < 0) { } \
else while (sd_hwdb_enumerate(hwdb, &(key), &(value)) > 0)
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_hwdb, sd_hwdb_unref);
+
+_SD_END_DECLARATIONS;
+
#endif
diff --git a/src/systemd/sd-icmp6-nd.h b/src/systemd/sd-icmp6-nd.h
deleted file mode 100644
index cb6c24a0cb..0000000000
--- a/src/systemd/sd-icmp6-nd.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foosdicmp6ndfoo
-#define foosdicmp6ndfoo
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 Intel Corporation. All rights reserved.
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General 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 <net/ethernet.h>
-
-#include "sd-event.h"
-
-enum {
- SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_NONE = 0,
- SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_TIMEOUT = 1,
- SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_OTHER = 2,
- SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_MANAGED = 3,
- SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_PREFIX_EXPIRED = 4,
-};
-
-typedef struct sd_icmp6_nd sd_icmp6_nd;
-
-typedef void(*sd_icmp6_nd_callback_t)(sd_icmp6_nd *nd, int event,
- void *userdata);
-
-int sd_icmp6_nd_set_callback(sd_icmp6_nd *nd, sd_icmp6_nd_callback_t cb,
- void *userdata);
-int sd_icmp6_nd_set_index(sd_icmp6_nd *nd, int interface_index);
-int sd_icmp6_nd_set_mac(sd_icmp6_nd *nd, const struct ether_addr *mac_addr);
-
-int sd_icmp6_nd_attach_event(sd_icmp6_nd *nd, sd_event *event, int priority);
-int sd_icmp6_nd_detach_event(sd_icmp6_nd *nd);
-sd_event *sd_icmp6_nd_get_event(sd_icmp6_nd *nd);
-
-sd_icmp6_nd *sd_icmp6_nd_ref(sd_icmp6_nd *nd);
-sd_icmp6_nd *sd_icmp6_nd_unref(sd_icmp6_nd *nd);
-int sd_icmp6_nd_new(sd_icmp6_nd **ret);
-
-int sd_icmp6_prefix_match(struct in6_addr *prefix, uint8_t prefixlen,
- struct in6_addr *addr);
-
-int sd_icmp6_ra_get_mtu(sd_icmp6_nd *nd, uint32_t *mtu);
-int sd_icmp6_ra_get_prefixlen(sd_icmp6_nd *nd, const struct in6_addr *addr,
- uint8_t *prefixlen);
-int sd_icmp6_ra_get_expired_prefix(sd_icmp6_nd *nd, struct in6_addr **addr,
- uint8_t *prefixlen);
-
-int sd_icmp6_nd_stop(sd_icmp6_nd *nd);
-int sd_icmp6_router_solicitation_start(sd_icmp6_nd *nd);
-
-#define SD_ICMP6_ND_ADDRESS_FORMAT_STR "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x"
-
-#define SD_ICMP6_ND_ADDRESS_FORMAT_VAL(address) \
- be16toh((address).s6_addr16[0]), \
- be16toh((address).s6_addr16[1]), \
- be16toh((address).s6_addr16[2]), \
- be16toh((address).s6_addr16[3]), \
- be16toh((address).s6_addr16[4]), \
- be16toh((address).s6_addr16[5]), \
- be16toh((address).s6_addr16[6]), \
- be16toh((address).s6_addr16[7])
-
-#endif
diff --git a/src/systemd/sd-ipv4acd.h b/src/systemd/sd-ipv4acd.h
index adcb2c7b92..9f0e2ba7d4 100644
--- a/src/systemd/sd-ipv4acd.h
+++ b/src/systemd/sd-ipv4acd.h
@@ -23,12 +23,15 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdbool.h>
-#include <netinet/in.h>
#include <net/ethernet.h>
+#include <netinet/in.h>
#include "sd-event.h"
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
enum {
SD_IPV4ACD_EVENT_STOP = 0,
SD_IPV4ACD_EVENT_BIND = 1,
@@ -45,11 +48,15 @@ int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_cb_t cb, void *userdata);
int sd_ipv4acd_set_mac(sd_ipv4acd *ll, const struct ether_addr *addr);
int sd_ipv4acd_set_index(sd_ipv4acd *ll, int interface_index);
int sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address);
-bool sd_ipv4acd_is_running(sd_ipv4acd *ll);
+int sd_ipv4acd_is_running(sd_ipv4acd *ll);
int sd_ipv4acd_start(sd_ipv4acd *ll);
int sd_ipv4acd_stop(sd_ipv4acd *ll);
sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *ll);
sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *ll);
-int sd_ipv4acd_new (sd_ipv4acd **ret);
+int sd_ipv4acd_new(sd_ipv4acd **ret);
+
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ipv4acd, sd_ipv4acd_unref);
+
+_SD_END_DECLARATIONS;
#endif
diff --git a/src/systemd/sd-ipv4ll.h b/src/systemd/sd-ipv4ll.h
index 677505f0c6..8c2533d1e6 100644
--- a/src/systemd/sd-ipv4ll.h
+++ b/src/systemd/sd-ipv4ll.h
@@ -22,12 +22,15 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdbool.h>
-#include <netinet/in.h>
#include <net/ethernet.h>
+#include <netinet/in.h>
#include "sd-event.h"
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
enum {
SD_IPV4LL_EVENT_STOP = 0,
SD_IPV4LL_EVENT_BIND = 1,
@@ -43,12 +46,17 @@ int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address);
int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_cb_t cb, void *userdata);
int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr);
int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index);
+int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address);
int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, unsigned seed);
-bool sd_ipv4ll_is_running(sd_ipv4ll *ll);
+int sd_ipv4ll_is_running(sd_ipv4ll *ll);
int sd_ipv4ll_start(sd_ipv4ll *ll);
int sd_ipv4ll_stop(sd_ipv4ll *ll);
sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll);
sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll);
int sd_ipv4ll_new (sd_ipv4ll **ret);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ipv4ll, sd_ipv4ll_unref);
+
+_SD_END_DECLARATIONS;
+
#endif
diff --git a/src/systemd/sd-journal.h b/src/systemd/sd-journal.h
index 00237a2158..33e36149e9 100644
--- a/src/systemd/sd-journal.h
+++ b/src/systemd/sd-journal.h
@@ -23,12 +23,13 @@
***/
#include <inttypes.h>
-#include <sys/types.h>
#include <stdarg.h>
+#include <sys/types.h>
#include <sys/uio.h>
#include <syslog.h>
#include "sd-id128.h"
+
#include "_sd-common.h"
/* Journal APIs. See sd-journal(3) for more information. */
@@ -154,6 +155,8 @@ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **text);
#define SD_JOURNAL_FOREACH_UNIQUE(j, data, l) \
for (sd_journal_restart_unique(j); sd_journal_enumerate_unique((j), &(data), &(l)) > 0; )
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_journal, sd_journal_close);
+
_SD_END_DECLARATIONS;
#endif
diff --git a/src/systemd/sd-lldp.h b/src/systemd/sd-lldp.h
index 0680e526b0..d912b08a6a 100644
--- a/src/systemd/sd-lldp.h
+++ b/src/systemd/sd-lldp.h
@@ -1,5 +1,8 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+#ifndef foosdlldphfoo
+#define foosdlldphfoo
+
/***
This file is part of systemd.
@@ -20,20 +23,32 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#pragma once
+#include <inttypes.h>
+#include <net/ethernet.h>
#include "sd-event.h"
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
enum {
SD_LLDP_EVENT_UPDATE_INFO = 0,
};
+enum {
+ SD_LLDP_DESTINATION_TYPE_NEAREST_BRIDGE,
+ SD_LLDP_DESTINATION_TYPE_NEAREST_NON_TPMR_BRIDGE,
+ SD_LLDP_DESTINATION_TYPE_NEAREST_CUSTOMER_BRIDGE,
+};
+
typedef struct sd_lldp sd_lldp;
+typedef struct sd_lldp_packet sd_lldp_packet;
typedef void (*sd_lldp_cb_t)(sd_lldp *lldp, int event, void *userdata);
int sd_lldp_new(int ifindex, const char *ifname, const struct ether_addr *mac, sd_lldp **ret);
-void sd_lldp_free(sd_lldp *lldp);
+sd_lldp* sd_lldp_unref(sd_lldp *lldp);
int sd_lldp_start(sd_lldp *lldp);
int sd_lldp_stop(sd_lldp *lldp);
@@ -43,3 +58,32 @@ int sd_lldp_detach_event(sd_lldp *lldp);
int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_cb_t cb, void *userdata);
int sd_lldp_save(sd_lldp *lldp, const char *file);
+
+int sd_lldp_packet_read_chassis_id(sd_lldp_packet *tlv, uint8_t *type, uint8_t **data, uint16_t *length);
+int sd_lldp_packet_read_port_id(sd_lldp_packet *tlv, uint8_t *type, uint8_t **data, uint16_t *length);
+int sd_lldp_packet_read_ttl(sd_lldp_packet *tlv, uint16_t *ttl);
+int sd_lldp_packet_read_system_name(sd_lldp_packet *tlv, char **data, uint16_t *length);
+int sd_lldp_packet_read_system_description(sd_lldp_packet *tlv, char **data, uint16_t *length);
+int sd_lldp_packet_read_system_capability(sd_lldp_packet *tlv, uint16_t *data);
+int sd_lldp_packet_read_port_description(sd_lldp_packet *tlv, char **data, uint16_t *length);
+
+/* IEEE 802.1 organizationally specific TLVs */
+int sd_lldp_packet_read_port_vlan_id(sd_lldp_packet *tlv, uint16_t *id);
+int sd_lldp_packet_read_port_protocol_vlan_id(sd_lldp_packet *tlv, uint8_t *flags, uint16_t *id);
+int sd_lldp_packet_read_vlan_name(sd_lldp_packet *tlv, uint16_t *vlan_id, char **name, uint16_t *length);
+int sd_lldp_packet_read_management_vid(sd_lldp_packet *tlv, uint16_t *id);
+int sd_lldp_packet_read_link_aggregation(sd_lldp_packet *tlv, uint8_t *status, uint32_t *id);
+
+sd_lldp_packet *sd_lldp_packet_ref(sd_lldp_packet *tlv);
+sd_lldp_packet *sd_lldp_packet_unref(sd_lldp_packet *tlv);
+
+int sd_lldp_packet_get_destination_type(sd_lldp_packet *tlv, int *dest);
+
+int sd_lldp_get_packets(sd_lldp *lldp, sd_lldp_packet ***tlvs);
+
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp, sd_lldp_unref);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp_packet, sd_lldp_packet_unref);
+
+_SD_END_DECLARATIONS;
+
+#endif
diff --git a/src/systemd/sd-login.h b/src/systemd/sd-login.h
index 59c6eedcda..164c75641c 100644
--- a/src/systemd/sd-login.h
+++ b/src/systemd/sd-login.h
@@ -22,8 +22,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
#include <inttypes.h>
+#include <sys/types.h>
#include "_sd-common.h"
@@ -240,6 +240,8 @@ int sd_login_monitor_get_events(sd_login_monitor *m);
/* Get timeout for poll(), as usec value relative to CLOCK_MONOTONIC's epoch */
int sd_login_monitor_get_timeout(sd_login_monitor *m, uint64_t *timeout_usec);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_login_monitor, sd_login_monitor_unref);
+
_SD_END_DECLARATIONS;
#endif
diff --git a/src/systemd/sd-messages.h b/src/systemd/sd-messages.h
index 8aedaec6d1..1183df6105 100644
--- a/src/systemd/sd-messages.h
+++ b/src/systemd/sd-messages.h
@@ -23,6 +23,7 @@
***/
#include "sd-id128.h"
+
#include "_sd-common.h"
_SD_BEGIN_DECLARATIONS;
@@ -85,6 +86,9 @@ _SD_BEGIN_DECLARATIONS;
#define SD_MESSAGE_BOOTCHART SD_ID128_MAKE(9f,26,aa,56,2c,f4,40,c2,b1,6c,77,3d,04,79,b5,18)
+#define SD_MESSAGE_DNSSEC_FAILURE SD_ID128_MAKE(16,75,d7,f1,72,17,40,98,b1,10,8b,f8,c7,dc,8f,5d)
+#define SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED SD_ID128_MAKE(4d,44,08,cf,d0,d1,44,85,91,84,d1,e6,5d,7c,8a,65)
+
_SD_END_DECLARATIONS;
#endif
diff --git a/src/systemd/sd-ndisc.h b/src/systemd/sd-ndisc.h
new file mode 100644
index 0000000000..d48612efdf
--- /dev/null
+++ b/src/systemd/sd-ndisc.h
@@ -0,0 +1,86 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosdndiscfoo
+#define foosdndiscfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Intel Corporation. All rights reserved.
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <net/ethernet.h>
+
+#include "sd-event.h"
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+enum {
+ SD_NDISC_EVENT_STOP = 0,
+ SD_NDISC_EVENT_TIMEOUT = 1,
+};
+
+typedef struct sd_ndisc sd_ndisc;
+
+typedef void(*sd_ndisc_router_callback_t)(sd_ndisc *nd, uint8_t flags, const struct in6_addr *gateway, unsigned lifetime, int pref, void *userdata);
+typedef void(*sd_ndisc_prefix_onlink_callback_t)(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen,
+ unsigned lifetime, void *userdata);
+typedef void(*sd_ndisc_prefix_autonomous_callback_t)(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen,
+ unsigned lifetime_prefered, unsigned lifetime_valid, void *userdata);
+typedef void(*sd_ndisc_callback_t)(sd_ndisc *nd, int event, void *userdata);
+
+int sd_ndisc_set_callback(sd_ndisc *nd,
+ sd_ndisc_router_callback_t rcb,
+ sd_ndisc_prefix_onlink_callback_t plcb,
+ sd_ndisc_prefix_autonomous_callback_t pacb,
+ sd_ndisc_callback_t cb,
+ void *userdata);
+int sd_ndisc_set_index(sd_ndisc *nd, int interface_index);
+int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr);
+
+int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int priority);
+int sd_ndisc_detach_event(sd_ndisc *nd);
+sd_event *sd_ndisc_get_event(sd_ndisc *nd);
+
+sd_ndisc *sd_ndisc_ref(sd_ndisc *nd);
+sd_ndisc *sd_ndisc_unref(sd_ndisc *nd);
+int sd_ndisc_new(sd_ndisc **ret);
+
+int sd_ndisc_get_mtu(sd_ndisc *nd, uint32_t *mtu);
+
+int sd_ndisc_stop(sd_ndisc *nd);
+int sd_ndisc_router_discovery_start(sd_ndisc *nd);
+
+#define SD_NDISC_ADDRESS_FORMAT_STR "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x"
+
+#define SD_NDISC_ADDRESS_FORMAT_VAL(address) \
+ be16toh((address).s6_addr16[0]), \
+ be16toh((address).s6_addr16[1]), \
+ be16toh((address).s6_addr16[2]), \
+ be16toh((address).s6_addr16[3]), \
+ be16toh((address).s6_addr16[4]), \
+ be16toh((address).s6_addr16[5]), \
+ be16toh((address).s6_addr16[6]), \
+ be16toh((address).s6_addr16[7])
+
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc, sd_ndisc_unref);
+
+_SD_END_DECLARATIONS;
+
+#endif
diff --git a/src/systemd/sd-netlink.h b/src/systemd/sd-netlink.h
index cb462bf48f..98088f1204 100644
--- a/src/systemd/sd-netlink.h
+++ b/src/systemd/sd-netlink.h
@@ -23,12 +23,13 @@
***/
#include <inttypes.h>
-#include <netinet/in.h>
#include <netinet/ether.h>
+#include <netinet/in.h>
#include <linux/rtnetlink.h>
#include <linux/neighbour.h>
#include "sd-event.h"
+
#include "_sd-common.h"
_SD_BEGIN_DECLARATIONS;
@@ -104,6 +105,7 @@ int sd_netlink_message_request_dump(sd_netlink_message *m, int dump);
int sd_netlink_message_is_error(sd_netlink_message *m);
int sd_netlink_message_get_errno(sd_netlink_message *m);
int sd_netlink_message_get_type(sd_netlink_message *m, uint16_t *type);
+int sd_netlink_message_set_flags(sd_netlink_message *m, uint16_t flags);
int sd_netlink_message_is_broadcast(sd_netlink_message *m);
/* rtnl */
@@ -135,7 +137,13 @@ int sd_rtnl_message_link_get_type(sd_netlink_message *m, unsigned *type);
int sd_rtnl_message_route_set_dst_prefixlen(sd_netlink_message *m, unsigned char prefixlen);
int sd_rtnl_message_route_set_src_prefixlen(sd_netlink_message *m, unsigned char prefixlen);
int sd_rtnl_message_route_set_scope(sd_netlink_message *m, unsigned char scope);
+int sd_rtnl_message_route_set_flags(sd_netlink_message *m, unsigned flags);
+int sd_rtnl_message_route_get_flags(sd_netlink_message *m, unsigned *flags);
int sd_rtnl_message_route_get_family(sd_netlink_message *m, int *family);
+int sd_rtnl_message_route_get_protocol(sd_netlink_message *m, unsigned char *protocol);
+int sd_rtnl_message_route_get_scope(sd_netlink_message *m, unsigned char *scope);
+int sd_rtnl_message_route_get_tos(sd_netlink_message *m, unsigned char *tos);
+int sd_rtnl_message_route_get_table(sd_netlink_message *m, unsigned char *table);
int sd_rtnl_message_route_get_dst_prefixlen(sd_netlink_message *m, unsigned char *dst_len);
int sd_rtnl_message_route_get_src_prefixlen(sd_netlink_message *m, unsigned char *src_len);
@@ -146,6 +154,9 @@ int sd_rtnl_message_neigh_get_ifindex(sd_netlink_message *m, int *family);
int sd_rtnl_message_neigh_get_state(sd_netlink_message *m, uint16_t *state);
int sd_rtnl_message_neigh_get_flags(sd_netlink_message *m, uint8_t *flags);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_netlink, sd_netlink_unref);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_netlink_message, sd_netlink_message_unref);
+
_SD_END_DECLARATIONS;
#endif
diff --git a/src/systemd/sd-network.h b/src/systemd/sd-network.h
index 4179015fbf..653c61a162 100644
--- a/src/systemd/sd-network.h
+++ b/src/systemd/sd-network.h
@@ -23,8 +23,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
#include <inttypes.h>
+#include <sys/types.h>
#include "_sd-common.h"
@@ -111,6 +111,27 @@ int sd_network_link_get_ntp(int ifindex, char ***addr);
*/
int sd_network_link_get_llmnr(int ifindex, char **llmnr);
+/* Indicates whether or not MulticastDNS should be enabled for the
+ * link.
+ * Possible levels of support: yes, no, resolve
+ * Possible return codes:
+ * -ENODATA: networkd is not aware of the link
+ */
+int sd_network_link_get_mdns(int ifindex, char **mdns);
+
+/* Indicates whether or not DNSSEC should be enabled for the link
+ * Possible levels of support: yes, no, allow-downgrade
+ * Possible return codes:
+ * -ENODATA: networkd is not aware of the link
+ */
+int sd_network_link_get_dnssec(int ifindex, char **dnssec);
+
+/* Returns the list of per-interface DNSSEC negative trust anchors
+ * Possible return codes:
+ * -ENODATA: networkd is not aware of the link, or has no such data
+ */
+int sd_network_link_get_dnssec_negative_trust_anchors(int ifindex, char ***nta);
+
int sd_network_link_get_lldp(int ifindex, char **lldp);
/* Get the DNS domain names for a given link. */
@@ -150,6 +171,8 @@ int sd_network_monitor_get_events(sd_network_monitor *m);
/* Get timeout for poll(), as usec value relative to CLOCK_MONOTONIC's epoch */
int sd_network_monitor_get_timeout(sd_network_monitor *m, uint64_t *timeout_usec);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_network_monitor, sd_network_monitor_unref);
+
_SD_END_DECLARATIONS;
#endif
diff --git a/src/systemd/sd-path.h b/src/systemd/sd-path.h
index e238c0ce20..3280303633 100644
--- a/src/systemd/sd-path.h
+++ b/src/systemd/sd-path.h
@@ -24,6 +24,10 @@
#include <inttypes.h>
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
enum {
/* Temporary files */
SD_PATH_TEMPORARY = 0x0ULL,
@@ -84,4 +88,6 @@ enum {
int sd_path_home(uint64_t type, const char *suffix, char **path);
int sd_path_search(uint64_t type, const char *suffix, char ***paths);
+_SD_END_DECLARATIONS;
+
#endif
diff --git a/src/systemd/sd-pppoe.h b/src/systemd/sd-pppoe.h
deleted file mode 100644
index 90878ffa27..0000000000
--- a/src/systemd/sd-pppoe.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foosdpppoefoo
-#define foosdpppoefoo
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 Tom Gundersen
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General 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 <net/ethernet.h>
-
-#include "sd-event.h"
-
-#include "sparse-endian.h"
-
-enum {
- SD_PPPOE_EVENT_RUNNING = 0,
- SD_PPPOE_EVENT_STOPPED = 1,
-};
-
-typedef struct sd_pppoe sd_pppoe;
-typedef void (*sd_pppoe_cb_t)(sd_pppoe *ppp, int event, void *userdata);
-
-int sd_pppoe_detach_event(sd_pppoe *ppp);
-int sd_pppoe_attach_event(sd_pppoe *ppp, sd_event *event, int priority);
-int sd_pppoe_get_channel(sd_pppoe *ppp, int *channel);
-int sd_pppoe_set_callback(sd_pppoe *ppp, sd_pppoe_cb_t cb, void *userdata);
-int sd_pppoe_set_ifindex(sd_pppoe *ppp, int ifindex);
-int sd_pppoe_set_ifname(sd_pppoe *ppp, const char *ifname);
-int sd_pppoe_set_service_name(sd_pppoe *ppp, const char *service_name);
-int sd_pppoe_start(sd_pppoe *ppp);
-int sd_pppoe_stop(sd_pppoe *ppp);
-sd_pppoe *sd_pppoe_ref(sd_pppoe *ppp);
-sd_pppoe *sd_pppoe_unref(sd_pppoe *ppp);
-int sd_pppoe_new (sd_pppoe **ret);
-
-#endif
diff --git a/src/systemd/sd-resolve.h b/src/systemd/sd-resolve.h
index 80c5852e45..eb4548a2dc 100644
--- a/src/systemd/sd-resolve.h
+++ b/src/systemd/sd-resolve.h
@@ -22,13 +22,15 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
-#include <sys/socket.h>
+#include <inttypes.h>
#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/types.h>
-#include "_sd-common.h"
#include "sd-event.h"
+#include "_sd-common.h"
+
_SD_BEGIN_DECLARATIONS;
/* An opaque sd-resolve session structure */
@@ -42,9 +44,9 @@ typedef int (*sd_resolve_getaddrinfo_handler_t)(sd_resolve_query *q, int ret, co
typedef int (*sd_resolve_getnameinfo_handler_t)(sd_resolve_query *q, int ret, const char *host, const char *serv, void *userdata);
enum {
- SD_RESOLVE_GET_HOST = 1ULL,
- SD_RESOLVE_GET_SERVICE = 2ULL,
- SD_RESOLVE_GET_BOTH = 3ULL
+ SD_RESOLVE_GET_HOST = UINT64_C(1),
+ SD_RESOLVE_GET_SERVICE = UINT64_C(2),
+ SD_RESOLVE_GET_BOTH = UINT64_C(3),
};
int sd_resolve_default(sd_resolve **ret);
@@ -109,6 +111,9 @@ void *sd_resolve_query_set_userdata(sd_resolve_query *q, void *userdata);
sd_resolve *sd_resolve_query_get_resolve(sd_resolve_query *q);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_resolve, sd_resolve_unref);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_resolve_query, sd_resolve_query_unref);
+
_SD_END_DECLARATIONS;
#endif
diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c
index c40f833788..90a2111ec5 100644
--- a/src/sysusers/sysusers.c
+++ b/src/sysusers/sysusers.c
@@ -19,26 +19,31 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <pwd.h>
+#include <getopt.h>
#include <grp.h>
-#include <shadow.h>
#include <gshadow.h>
-#include <getopt.h>
+#include <pwd.h>
+#include <shadow.h>
#include <utmp.h>
-#include "util.h"
-#include "hashmap.h"
-#include "specifier.h"
-#include "path-util.h"
-#include "build.h"
-#include "strv.h"
+#include "alloc-util.h"
#include "conf-files.h"
#include "copy.h"
-#include "utf8.h"
+#include "def.h"
+#include "fd-util.h"
#include "fileio-label.h"
-#include "uid-range.h"
-#include "selinux-util.h"
#include "formats-util.h"
+#include "hashmap.h"
+#include "path-util.h"
+#include "selinux-util.h"
+#include "smack-util.h"
+#include "specifier.h"
+#include "string-util.h"
+#include "strv.h"
+#include "uid-range.h"
+#include "user-util.h"
+#include "utf8.h"
+#include "util.h"
typedef enum ItemType {
ADD_USER = 'u',
@@ -67,7 +72,7 @@ typedef struct Item {
static char *arg_root = NULL;
-static const char conf_file_dirs[] = CONF_DIRS_NULSTR("sysusers");
+static const char conf_file_dirs[] = CONF_PATHS_NULSTR("sysusers.d");
static Hashmap *users = NULL, *groups = NULL;
static Hashmap *todo_uids = NULL, *todo_gids = NULL;
@@ -275,7 +280,7 @@ static int putgrent_with_members(const struct group *gr, FILE *group) {
errno = 0;
if (putgrent(&t, group) != 0)
- return errno ? -errno : -EIO;
+ return errno > 0 ? -errno : -EIO;
return 1;
}
@@ -283,7 +288,7 @@ static int putgrent_with_members(const struct group *gr, FILE *group) {
errno = 0;
if (putgrent(gr, group) != 0)
- return errno ? -errno : -EIO;
+ return errno > 0 ? -errno : -EIO;
return 0;
}
@@ -325,7 +330,7 @@ static int putsgent_with_members(const struct sgrp *sg, FILE *gshadow) {
errno = 0;
if (putsgent(&t, gshadow) != 0)
- return errno ? -errno : -EIO;
+ return errno > 0 ? -errno : -EIO;
return 1;
}
@@ -333,7 +338,7 @@ static int putsgent_with_members(const struct sgrp *sg, FILE *gshadow) {
errno = 0;
if (putsgent(sg, gshadow) != 0)
- return errno ? -errno : -EIO;
+ return errno > 0 ? -errno : -EIO;
return 0;
}
@@ -353,6 +358,19 @@ static int sync_rights(FILE *from, FILE *to) {
return 0;
}
+static int rename_and_apply_smack(const char *temp_path, const char *dest_path) {
+ int r = 0;
+ if (rename(temp_path, dest_path) < 0)
+ return -errno;
+
+#ifdef SMACK_RUN_LABEL
+ r = mac_smack_apply(dest_path, SMACK_ATTR_ACCESS, SMACK_FLOOR_LABEL);
+ if (r < 0)
+ return r;
+#endif
+ return r;
+}
+
static int write_files(void) {
_cleanup_fclose_ FILE *passwd = NULL, *group = NULL, *shadow = NULL, *gshadow = NULL;
@@ -704,36 +722,32 @@ static int write_files(void) {
/* And make the new files count */
if (group_changed) {
if (group) {
- if (rename(group_tmp, group_path) < 0) {
- r = -errno;
+ r = rename_and_apply_smack(group_tmp, group_path);
+ if (r < 0)
goto finish;
- }
group_tmp = mfree(group_tmp);
}
if (gshadow) {
- if (rename(gshadow_tmp, gshadow_path) < 0) {
- r = -errno;
+ r = rename_and_apply_smack(gshadow_tmp, gshadow_path);
+ if (r < 0)
goto finish;
- }
gshadow_tmp = mfree(gshadow_tmp);
}
}
if (passwd) {
- if (rename(passwd_tmp, passwd_path) < 0) {
- r = -errno;
+ r = rename_and_apply_smack(passwd_tmp, passwd_path);
+ if (r < 0)
goto finish;
- }
passwd_tmp = mfree(passwd_tmp);
}
if (shadow) {
- if (rename(shadow_tmp, shadow_path) < 0) {
- r = -errno;
+ r = rename_and_apply_smack(shadow_tmp, shadow_path);
+ if (r < 0)
goto finish;
- }
shadow_tmp = mfree(shadow_tmp);
}
@@ -934,7 +948,7 @@ static int add_user(Item *i) {
}
}
- /* Otherwise try to reuse the group ID */
+ /* Otherwise, try to reuse the group ID */
if (!i->uid_set && i->gid_set) {
r = uid_is_ok((uid_t) i->gid, i->name);
if (r < 0)
@@ -1758,7 +1772,7 @@ static int parse_argv(int argc, char *argv[]) {
{}
};
- int c;
+ int c, r;
assert(argc >= 0);
assert(argv);
@@ -1772,17 +1786,12 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case ARG_ROOT:
- free(arg_root);
- arg_root = path_make_absolute_cwd(optarg);
- if (!arg_root)
- return log_oom();
-
- path_kill_slashes(arg_root);
+ r = parse_path_argument_and_warn(optarg, true, &arg_root);
+ if (r < 0)
+ return r;
break;
case '?':
@@ -1857,7 +1866,7 @@ int main(int argc, char *argv[]) {
if (r < 0)
goto finish;
- lock = take_password_lock(arg_root);
+ lock = take_etc_passwd_lock(arg_root);
if (lock < 0) {
log_error_errno(lock, "Failed to take lock: %m");
goto finish;
diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c
index 964750076a..d48d5abbe3 100644
--- a/src/sysv-generator/sysv-generator.c
+++ b/src/sysv-generator/sysv-generator.c
@@ -25,17 +25,24 @@
#include <stdio.h>
#include <unistd.h>
-#include "util.h"
+#include "alloc-util.h"
+#include "dirent-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "hashmap.h"
+#include "hexdecoct.h"
+#include "install.h"
+#include "log.h"
#include "mkdir.h"
-#include "strv.h"
-#include "path-util.h"
#include "path-lookup.h"
-#include "log.h"
-#include "unit-name.h"
-#include "special.h"
-#include "hashmap.h"
+#include "path-util.h"
#include "set.h"
-#include "install.h"
+#include "special.h"
+#include "stat-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "unit-name.h"
+#include "util.h"
typedef enum RunlevelType {
RUNLEVEL_UP,
@@ -80,9 +87,13 @@ typedef struct SysvStub {
char **conflicts;
bool has_lsb;
bool reload;
+ bool loaded;
} SysvStub;
static void free_sysvstub(SysvStub *s) {
+ if (!s)
+ return;
+
free(s->name);
free(s->path);
free(s->description);
@@ -107,19 +118,14 @@ static void free_sysvstub_hashmapp(Hashmap **h) {
}
static int add_symlink(const char *service, const char *where) {
- _cleanup_free_ char *from = NULL, *to = NULL;
+ const char *from, *to;
int r;
assert(service);
assert(where);
- from = strjoin(arg_dest, "/", service, NULL);
- if (!from)
- return log_oom();
-
- to = strjoin(arg_dest, "/", where, ".wants/", service, NULL);
- if (!to)
- return log_oom();
+ from = strjoina(arg_dest, "/", service);
+ to = strjoina(arg_dest, "/", where, ".wants/", service);
mkdir_parents_label(to, 0755);
@@ -127,6 +133,7 @@ static int add_symlink(const char *service, const char *where) {
if (r < 0) {
if (errno == EEXIST)
return 0;
+
return -errno;
}
@@ -134,20 +141,19 @@ static int add_symlink(const char *service, const char *where) {
}
static int add_alias(const char *service, const char *alias) {
- _cleanup_free_ char *link = NULL;
+ const char *link;
int r;
assert(service);
assert(alias);
- link = strjoin(arg_dest, "/", alias, NULL);
- if (!link)
- return log_oom();
+ link = strjoina(arg_dest, "/", alias);
r = symlink(service, link);
if (r < 0) {
if (errno == EEXIST)
return 0;
+
return -errno;
}
@@ -155,26 +161,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,
- *before = NULL, *after = NULL,
- *wants = NULL, *conflicts = NULL;
+ const char *unit;
+ char **p;
int r;
- before = strv_join(s->before, " ");
- after = strv_join(s->after, " ");
- wants = strv_join(s->wants, " ");
- conflicts = strv_join(s->conflicts, " ");
- unit = strjoin(arg_dest, "/", s->name, NULL);
- if (!before || !after || !wants || !conflicts || !unit)
- return log_oom();
+ assert(s);
+
+ if (!s->loaded)
+ return 0;
+
+ unit = strjoina(arg_dest, "/", s->name);
/* 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) > 0) {
- log_warning("Overwriting existing symlink %s with real service", unit);
+ log_warning("Overwriting existing symlink %s with real service.", unit);
(void) unlink(unit);
}
@@ -186,18 +189,20 @@ static int generate_unit_file(SysvStub *s) {
"# Automatically generated by systemd-sysv-generator\n\n"
"[Unit]\n"
"Documentation=man:systemd-sysv-generator(8)\n"
- "SourcePath=%s\n"
- "Description=%s\n",
- s->path, s->description);
-
- if (!isempty(before))
- fprintf(f, "Before=%s\n", before);
- if (!isempty(after))
- fprintf(f, "After=%s\n", after);
- if (!isempty(wants))
- fprintf(f, "Wants=%s\n", wants);
- if (!isempty(conflicts))
- fprintf(f, "Conflicts=%s\n", conflicts);
+ "SourcePath=%s\n",
+ s->path);
+
+ if (s->description)
+ fprintf(f, "Description=%s\n", s->description);
+
+ STRV_FOREACH(p, s->before)
+ fprintf(f, "Before=%s\n", *p);
+ STRV_FOREACH(p, s->after)
+ fprintf(f, "After=%s\n", *p);
+ STRV_FOREACH(p, s->wants)
+ fprintf(f, "Wants=%s\n", *p);
+ STRV_FOREACH(p, s->conflicts)
+ fprintf(f, "Conflicts=%s\n", *p);
fprintf(f,
"\n[Service]\n"
@@ -221,13 +226,17 @@ static int generate_unit_file(SysvStub *s) {
if (s->reload)
fprintf(f, "ExecReload=%s reload\n", s->path);
+ r = fflush_and_check(f);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write unit %s: %m", unit);
+
STRV_FOREACH(p, s->wanted_by) {
r = add_symlink(s->name, *p);
if (r < 0)
- log_error_errno(r, "Failed to create 'Wants' symlink to %s: %m", *p);
+ log_warning_errno(r, "Failed to create 'Wants' symlink to %s, ignoring: %m", *p);
}
- return 0;
+ return 1;
}
static bool usage_contains_reload(const char *line) {
@@ -257,7 +266,7 @@ static char *sysv_translate_name(const char *name) {
return res;
}
-static int sysv_translate_facility(const char *name, const char *filename, char **_r) {
+static int sysv_translate_facility(const char *name, const char *filename, char **ret) {
/* We silently ignore the $ prefix here. According to the LSB
* spec it simply indicates whether something is a
@@ -276,31 +285,45 @@ static int sysv_translate_facility(const char *name, const char *filename, char
"time", SPECIAL_TIME_SYNC_TARGET,
};
- char *filename_no_sh, *e, *r;
+ char *filename_no_sh, *e, *m;
const char *n;
unsigned i;
+ int r;
assert(name);
- assert(_r);
+ assert(filename);
+ assert(ret);
n = *name == '$' ? name + 1 : name;
for (i = 0; i < ELEMENTSOF(table); i += 2) {
-
if (!streq(table[i], n))
continue;
if (!table[i+1])
return 0;
- r = strdup(table[i+1]);
- if (!r)
+ m = strdup(table[i+1]);
+ if (!m)
return log_oom();
- goto finish;
+ *ret = m;
+ return 1;
+ }
+
+ /* If we don't know this name, fallback heuristics to figure
+ * out whether something is a target or a service alias. */
+
+ /* Facilities starting with $ are most likely targets */
+ if (*name == '$') {
+ r = unit_name_build(n, NULL, ".target", ret);
+ if (r < 0)
+ return log_error_errno(r, "Failed to build name: %m");
+
+ return r;
}
- /* strip ".sh" suffix from file name for comparison */
+ /* Strip ".sh" suffix from file name for comparison */
filename_no_sh = strdupa(filename);
e = endswith(filename_no_sh, ".sh");
if (e) {
@@ -308,103 +331,103 @@ static int sysv_translate_facility(const char *name, const char *filename, char
filename = filename_no_sh;
}
- /* If we don't know this name, fallback heuristics to figure
- * out whether something is a target or a service alias. */
-
- if (*name == '$') {
- int k;
-
- /* Facilities starting with $ are most likely targets */
- 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 */
+ /* Names equaling the file name of the services are redundant */
+ if (streq_ptr(n, filename))
return 0;
- else
- /* Everything else we assume to be normal service names */
- r = sysv_translate_name(n);
- if (!r)
- return -ENOMEM;
-finish:
- *_r = r;
+ /* Everything else we assume to be normal service names */
+ m = sysv_translate_name(n);
+ if (!m)
+ return log_oom();
+ *ret = m;
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;
- UnitType t;
+ assert(s);
+ assert(full_text);
+ assert(text);
- n = strndup(word, z);
- if (!n)
- return log_oom();
+ for (;;) {
+ _cleanup_free_ char *word = NULL, *m = NULL;
- r = sysv_translate_facility(n, basename(s->path), &m);
+ r = extract_first_word(&text, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to parse word from provides string: %m");
if (r == 0)
+ break;
+
+ r = sysv_translate_facility(word, basename(s->path), &m);
+ if (r <= 0) /* continue on error */
continue;
- t = unit_name_to_type(m);
- if (t == UNIT_SERVICE) {
+ switch (unit_name_to_type(m)) {
+
+ case 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 if (t == UNIT_TARGET) {
+ break;
+
+ case UNIT_TARGET:
+
/* 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();
}
- } else if (t == _UNIT_TYPE_INVALID)
+
+ break;
+
+ case _UNIT_TYPE_INVALID:
log_warning("Unit name '%s' is invalid", m);
- else
+ break;
+
+ default:
log_warning("Unknown unit type for unit '%s'", m);
+ }
}
- 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;
+ assert(s);
+ assert(full_text);
+ assert(text);
- n = strndup(word, z);
- if (!n)
- return log_oom();
+ for (;;) {
+ _cleanup_free_ char *word = NULL, *m = NULL;
+ bool is_before;
- 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;
- }
+ r = extract_first_word(&text, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse word from provides string: %m");
if (r == 0)
+ break;
+
+ r = sysv_translate_facility(word, basename(s->path), &m);
+ if (r <= 0) /* continue on error */
continue;
is_before = startswith_no_case(full_text, "X-Start-Before:");
@@ -414,15 +437,14 @@ static int handle_dependencies(SysvStub *s, unsigned line, const char *full_text
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;
}
@@ -440,24 +462,22 @@ static int load_sysv(SysvStub *s) {
_cleanup_free_ char *short_description = NULL, *long_description = NULL, *chkconfig_description = NULL;
char *description;
bool supports_reload = false;
+ char l[LINE_MAX];
assert(s);
f = fopen(s->path, "re");
- if (!f)
- return errno == ENOENT ? 0 : -errno;
-
- log_debug("Loading SysV script %s", s->path);
+ if (!f) {
+ if (errno == ENOENT)
+ return 0;
- while (!feof(f)) {
- char l[LINE_MAX], *t;
+ return log_error_errno(errno, "Failed to open %s: %m", s->path);
+ }
- if (!fgets(l, sizeof(l), f)) {
- if (feof(f))
- break;
+ log_debug("Loading SysV script %s", s->path);
- return log_error_errno(errno, "Failed to read configuration file '%s': %m", s->path);
- }
+ FOREACH_LINE(l, f, goto fail) {
+ char *t;
line++;
@@ -500,29 +520,25 @@ static int load_sysv(SysvStub *s) {
if (startswith_no_case(t, "description:")) {
- size_t k = strlen(t);
- char *d;
+ size_t k;
const char *j;
- if (t[k-1] == '\\') {
+ k = strlen(t);
+ if (k > 0 && t[k-1] == '\\') {
state = DESCRIPTION;
t[k-1] = 0;
}
j = strstrip(t+12);
- if (j && *j) {
- d = strdup(j);
- if (!d)
- return -ENOMEM;
- } else
- d = NULL;
+ if (isempty(j))
+ j = NULL;
- free(chkconfig_description);
- chkconfig_description = d;
+ r = free_and_strdup(&chkconfig_description, j);
+ if (r < 0)
+ return log_oom();
} else if (startswith_no_case(t, "pidfile:")) {
-
- char *fn;
+ const char *fn;
state = NORMAL;
@@ -532,12 +548,9 @@ static int load_sysv(SysvStub *s) {
continue;
}
- fn = strdup(fn);
- if (!fn)
- return -ENOMEM;
-
- free(s->pid_file);
- s->pid_file = fn;
+ r = free_and_strdup(&s->pid_file, fn);
+ if (r < 0)
+ return log_oom();
}
} else if (state == DESCRIPTION) {
@@ -545,25 +558,25 @@ static int load_sysv(SysvStub *s) {
/* Try to parse Red Hat style description
* continuation */
- size_t k = strlen(t);
+ size_t k;
char *j;
- if (t[k-1] == '\\')
+ k = strlen(t);
+ if (k > 0 && t[k-1] == '\\')
t[k-1] = 0;
else
state = NORMAL;
j = strstrip(t);
- if (j && *j) {
+ if (!isempty(j)) {
char *d = NULL;
if (chkconfig_description)
d = strjoin(chkconfig_description, " ", j, NULL);
else
d = strdup(j);
-
if (!d)
- return -ENOMEM;
+ return log_oom();
free(chkconfig_description);
chkconfig_description = d;
@@ -577,6 +590,7 @@ static int load_sysv(SysvStub *s) {
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:") ||
@@ -588,55 +602,47 @@ static int load_sysv(SysvStub *s) {
if (r < 0)
return r;
-
} else if (startswith_no_case(t, "Description:")) {
- char *d, *j;
+ const char *j;
state = LSB_DESCRIPTION;
j = strstrip(t+12);
- if (j && *j) {
- d = strdup(j);
- if (!d)
- return -ENOMEM;
- } else
- d = NULL;
+ if (isempty(j))
+ j = NULL;
- free(long_description);
- long_description = d;
+ r = free_and_strdup(&long_description, j);
+ if (r < 0)
+ return log_oom();
} else if (startswith_no_case(t, "Short-Description:")) {
- char *d, *j;
+ const char *j;
state = LSB;
j = strstrip(t+18);
- if (j && *j) {
- d = strdup(j);
- if (!d)
- return -ENOMEM;
- } else
- d = NULL;
+ if (isempty(j))
+ j = NULL;
- free(short_description);
- short_description = d;
+ r = free_and_strdup(&short_description, j);
+ if (r < 0)
+ return log_oom();
} else if (state == LSB_DESCRIPTION) {
if (startswith(l, "#\t") || startswith(l, "# ")) {
- char *j;
+ const char *j;
j = strstrip(t);
- if (j && *j) {
+ if (!isempty(j)) {
char *d = NULL;
if (long_description)
d = strjoin(long_description, " ", t, NULL);
else
d = strdup(j);
-
if (!d)
- return -ENOMEM;
+ return log_oom();
free(long_description);
long_description = d;
@@ -667,12 +673,16 @@ static int load_sysv(SysvStub *s) {
d = strappend(s->has_lsb ? "LSB: " : "SYSV: ", description);
if (!d)
- return -ENOMEM;
+ return log_oom();
s->description = d;
}
+ s->loaded = true;
return 0;
+
+fail:
+ return log_error_errno(errno, "Failed to read configuration file '%s': %m", s->path);
}
static int fix_order(SysvStub *s, Hashmap *all_services) {
@@ -682,6 +692,9 @@ static int fix_order(SysvStub *s, Hashmap *all_services) {
assert(s);
+ if (!s->loaded)
+ return 0;
+
if (s->sysv_start_priority < 0)
return 0;
@@ -689,6 +702,9 @@ static int fix_order(SysvStub *s, Hashmap *all_services) {
if (s == other)
continue;
+ if (!other->loaded)
+ continue;
+
if (other->sysv_start_priority < 0)
continue;
@@ -701,13 +717,12 @@ static int fix_order(SysvStub *s, Hashmap *all_services) {
r = strv_extend(&s->after, other->name);
if (r < 0)
return log_oom();
- }
- else if (other->sysv_start_priority > s->sysv_start_priority) {
+
+ } else if (other->sysv_start_priority > s->sysv_start_priority) {
r = strv_extend(&s->before, other->name);
if (r < 0)
return log_oom();
- }
- else
+ } else
continue;
/* FIXME: Maybe we should compare the name here lexicographically? */
@@ -718,6 +733,10 @@ static int fix_order(SysvStub *s, Hashmap *all_services) {
static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) {
char **path;
+ int r;
+
+ assert(lp);
+ assert(all_services);
STRV_FOREACH(path, lp->sysvinit_path) {
_cleanup_closedir_ DIR *d = NULL;
@@ -726,21 +745,17 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) {
d = opendir(*path);
if (!d) {
if (errno != ENOENT)
- log_warning_errno(errno, "opendir(%s) failed: %m", *path);
+ log_warning_errno(errno, "Opening %s failed, ignoring: %m", *path);
continue;
}
- while ((de = readdir(d))) {
+ FOREACH_DIRENT(de, d, log_error_errno(errno, "Failed to enumerate directory %s, ignoring: %m", *path)) {
_cleanup_free_ char *fpath = NULL, *name = NULL;
_cleanup_(free_sysvstubp) SysvStub *service = NULL;
struct stat st;
- int r;
-
- if (hidden_file(de->d_name))
- continue;
if (fstatat(dirfd(d), de->d_name, &st, 0) < 0) {
- log_warning_errno(errno, "stat() failed on %s/%s: %m", *path, de->d_name);
+ log_warning_errno(errno, "stat() failed on %s/%s, ignoring: %m", *path, de->d_name);
continue;
}
@@ -757,15 +772,19 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) {
if (hashmap_contains(all_services, name))
continue;
+ r = unit_file_lookup_state(UNIT_FILE_SYSTEM, NULL, lp, name, NULL);
+ if (r < 0 && r != -ENOENT) {
+ log_debug_errno(r, "Failed to detect whether %s exists, skipping: %m", name);
+ continue;
+ } else if (r >= 0) {
+ log_debug("Native unit for %s already exists, skipping.", name);
+ continue;
+ }
+
fpath = strjoin(*path, "/", de->d_name, NULL);
if (!fpath)
return log_oom();
- if (unit_file_lookup_state(UNIT_FILE_SYSTEM, NULL, lp, name) >= 0) {
- log_debug("Native unit for %s already exists, skipping", name);
- continue;
- }
-
service = new0(SysvStub, 1);
if (!service)
return log_oom();
@@ -773,12 +792,12 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) {
service->sysv_start_priority = -1;
service->name = name;
service->path = fpath;
+ name = fpath = NULL;
r = hashmap_put(all_services, service->name, service);
if (r < 0)
return log_oom();
- name = fpath = NULL;
service = NULL;
}
}
@@ -787,43 +806,41 @@ static int enumerate_sysv(const 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;
- SysvStub *service;
- Iterator j;
Set *runlevel_services[ELEMENTSOF(rcnd_table)] = {};
_cleanup_set_free_ Set *shutdown_services = NULL;
- int r = 0;
+ SysvStub *service;
+ unsigned i;
+ Iterator j;
+ char **p;
+ int r;
+
+ assert(lp);
- STRV_FOREACH(p, lp->sysvrcnd_path)
+ STRV_FOREACH(p, lp->sysvrcnd_path) {
for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) {
+
+ _cleanup_closedir_ DIR *d = NULL;
+ _cleanup_free_ char *path = NULL;
struct dirent *de;
- free(path);
path = strjoin(*p, "/", rcnd_table[i].path, NULL);
- if (!path)
- return -ENOMEM;
-
- safe_closedir(d);
+ if (!path) {
+ r = log_oom();
+ goto finish;
+ }
d = opendir(path);
if (!d) {
if (errno != ENOENT)
- log_warning_errno(errno, "opendir(%s) failed: %m", path);
+ log_warning_errno(errno, "Opening %s failed, ignoring: %m", path);
continue;
}
- while ((de = readdir(d))) {
- _cleanup_free_ char *name = NULL;
-
+ FOREACH_DIRENT(de, d, log_error_errno(errno, "Failed to enumerate directory %s, ignoring: %m", path)) {
+ _cleanup_free_ char *name = NULL, *fpath = NULL;
int a, b;
- if (hidden_file(de->d_name))
- continue;
-
if (de->d_name[0] != 'S' && de->d_name[0] != 'K')
continue;
@@ -836,10 +853,9 @@ static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_servic
if (a < 0 || b < 0)
continue;
- free(fpath);
fpath = strjoin(*p, "/", de->d_name, NULL);
if (!fpath) {
- r = -ENOMEM;
+ r = log_oom();
goto finish;
}
@@ -851,64 +867,77 @@ static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_servic
service = hashmap_get(all_services, name);
if (!service){
- log_debug("Ignoring %s symlink in %s, not generating %s.",
- de->d_name, rcnd_table[i].path, name);
+ log_debug("Ignoring %s symlink in %s, not generating %s.", de->d_name, rcnd_table[i].path, name);
continue;
}
if (de->d_name[0] == 'S') {
- if (rcnd_table[i].type == RUNLEVEL_UP) {
- service->sysv_start_priority =
- MAX(a*10 + b, service->sysv_start_priority);
- }
+ if (rcnd_table[i].type == RUNLEVEL_UP)
+ service->sysv_start_priority = MAX(a*10 + b, service->sysv_start_priority);
r = set_ensure_allocated(&runlevel_services[i], NULL);
- if (r < 0)
+ if (r < 0) {
+ log_oom();
goto finish;
+ }
r = set_put(runlevel_services[i], service);
- if (r < 0)
+ if (r < 0) {
+ log_oom();
goto finish;
+ }
} else if (de->d_name[0] == 'K' &&
(rcnd_table[i].type == RUNLEVEL_DOWN)) {
r = set_ensure_allocated(&shutdown_services, NULL);
- if (r < 0)
+ if (r < 0) {
+ log_oom();
goto finish;
+ }
r = set_put(shutdown_services, service);
- if (r < 0)
+ if (r < 0) {
+ log_oom();
goto finish;
+ }
}
}
}
+ }
for (i = 0; i < ELEMENTSOF(rcnd_table); i ++)
SET_FOREACH(service, runlevel_services[i], j) {
r = strv_extend(&service->before, rcnd_table[i].target);
- if (r < 0)
- return log_oom();
+ if (r < 0) {
+ log_oom();
+ goto finish;
+ }
r = strv_extend(&service->wanted_by, rcnd_table[i].target);
- if (r < 0)
- return log_oom();
+ if (r < 0) {
+ log_oom();
+ goto finish;
+ }
}
SET_FOREACH(service, shutdown_services, j) {
r = strv_extend(&service->before, SPECIAL_SHUTDOWN_TARGET);
- if (r < 0)
- return log_oom();
+ if (r < 0) {
+ log_oom();
+ goto finish;
+ }
r = strv_extend(&service->conflicts, SPECIAL_SHUTDOWN_TARGET);
- if (r < 0)
- return log_oom();
+ if (r < 0) {
+ log_oom();
+ goto finish;
+ }
}
r = 0;
finish:
-
for (i = 0; i < ELEMENTSOF(rcnd_table); i++)
set_free(runlevel_services[i]);
@@ -916,11 +945,11 @@ finish:
}
int main(int argc, char *argv[]) {
- int r, q;
- _cleanup_lookup_paths_free_ LookupPaths lp = {};
_cleanup_(free_sysvstub_hashmapp) Hashmap *all_services = NULL;
+ _cleanup_lookup_paths_free_ LookupPaths lp = {};
SysvStub *service;
Iterator j;
+ int r;
if (argc > 1 && argc != 4) {
log_error("This program takes three or no arguments.");
@@ -938,43 +967,34 @@ int main(int argc, char *argv[]) {
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;
+ log_error_errno(r, "Failed to find lookup paths: %m");
+ goto finish;
}
all_services = hashmap_new(&string_hash_ops);
if (!all_services) {
- log_oom();
- return EXIT_FAILURE;
+ r = log_oom();
+ goto finish;
}
r = enumerate_sysv(&lp, all_services);
- if (r < 0) {
- log_error("Failed to generate units for all init scripts.");
- return EXIT_FAILURE;
- }
+ if (r < 0)
+ goto finish;
r = set_dependencies_from_rcnd(&lp, all_services);
- if (r < 0) {
- log_error("Failed to read runlevels from rcnd links.");
- return EXIT_FAILURE;
- }
+ if (r < 0)
+ goto finish;
- HASHMAP_FOREACH(service, all_services, j) {
- q = load_sysv(service);
- if (q < 0)
- continue;
- }
+ HASHMAP_FOREACH(service, all_services, j)
+ (void) load_sysv(service);
HASHMAP_FOREACH(service, all_services, j) {
- q = fix_order(service, all_services);
- if (q < 0)
- continue;
-
- q = generate_unit_file(service);
- if (q < 0)
- continue;
+ (void) fix_order(service, all_services);
+ (void) generate_unit_file(service);
}
- return EXIT_SUCCESS;
+ r = 0;
+
+finish:
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/test/test-acl-util.c b/src/test/test-acl-util.c
new file mode 100644
index 0000000000..91866daf2d
--- /dev/null
+++ b/src/test/test-acl-util.c
@@ -0,0 +1,87 @@
+/*-*- 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 <fcntl.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "acl-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "string-util.h"
+#include "user-util.h"
+
+static void test_add_acls_for_user(void) {
+ char fn[] = "/tmp/test-empty.XXXXXX";
+ _cleanup_close_ int fd = -1;
+ char *cmd;
+ uid_t uid;
+ int r;
+
+ fd = mkostemp_safe(fn, O_RDWR|O_CLOEXEC);
+ assert_se(fd >= 0);
+
+ /* Use the mode that user journal files use */
+ assert_se(fchmod(fd, 0640) == 0);
+
+ cmd = strjoina("ls -l ", fn);
+ assert_se(system(cmd) == 0);
+
+ cmd = strjoina("getfacl -p ", fn);
+ assert_se(system(cmd) == 0);
+
+ if (getuid() == 0) {
+ const char *nobody = "nobody";
+ r = get_user_creds(&nobody, &uid, NULL, NULL, NULL);
+ if (r < 0)
+ uid = 0;
+ } else
+ uid = getuid();
+
+ r = add_acls_for_user(fd, uid);
+ assert_se(r >= 0);
+
+ cmd = strjoina("ls -l ", fn);
+ assert_se(system(cmd) == 0);
+
+ cmd = strjoina("getfacl -p ", fn);
+ assert_se(system(cmd) == 0);
+
+ /* set the acls again */
+
+ r = add_acls_for_user(fd, uid);
+ assert_se(r >= 0);
+
+ cmd = strjoina("ls -l ", fn);
+ assert_se(system(cmd) == 0);
+
+ cmd = strjoina("getfacl -p ", fn);
+ assert_se(system(cmd) == 0);
+
+ unlink(fn);
+}
+
+int main(int argc, char **argv) {
+ test_add_acls_for_user();
+
+ return 0;
+}
diff --git a/src/test/test-af-list.c b/src/test/test-af-list.c
index d69104f540..aeaa0929b1 100644
--- a/src/test/test-af-list.c
+++ b/src/test/test-af-list.c
@@ -17,17 +17,18 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/socket.h>
#include <string.h>
+#include <sys/socket.h>
#include "macro.h"
+#include "string-util.h"
#include "util.h"
static const struct af_name* lookup_af(register const char *str, register unsigned int len);
+#include "af-from-name.h"
#include "af-list.h"
#include "af-to-name.h"
-#include "af-from-name.h"
int main(int argc, const char *argv[]) {
@@ -45,4 +46,4 @@ int main(int argc, const char *argv[]) {
assert_se(af_from_name("huddlduddl") == AF_UNSPEC);
return 0;
-} \ No newline at end of file
+}
diff --git a/src/test/test-architecture.c b/src/test/test-architecture.c
index a5b66a7d2f..35479d67c1 100644
--- a/src/test/test-architecture.c
+++ b/src/test/test-architecture.c
@@ -19,10 +19,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "virt.h"
#include "architecture.h"
-#include "util.h"
#include "log.h"
+#include "util.h"
+#include "virt.h"
int main(int argc, char *argv[]) {
int a, v;
diff --git a/src/test/test-arphrd-list.c b/src/test/test-arphrd-list.c
index d7c8eaa4a9..f3989ad201 100644
--- a/src/test/test-arphrd-list.c
+++ b/src/test/test-arphrd-list.c
@@ -21,13 +21,14 @@
#include <string.h>
#include "macro.h"
+#include "string-util.h"
#include "util.h"
static const struct arphrd_name* lookup_arphrd(register const char *str, register unsigned int len);
+#include "arphrd-from-name.h"
#include "arphrd-list.h"
#include "arphrd-to-name.h"
-#include "arphrd-from-name.h"
int main(int argc, const char *argv[]) {
@@ -45,4 +46,4 @@ int main(int argc, const char *argv[]) {
assert_se(arphrd_from_name("huddlduddl") == 0);
return 0;
-} \ No newline at end of file
+}
diff --git a/src/test/test-async.c b/src/test/test-async.c
index abd36d693c..ada6d67c42 100644
--- a/src/test/test-async.c
+++ b/src/test/test-async.c
@@ -20,8 +20,9 @@
#include <unistd.h>
#include "async.h"
-#include "util.h"
+#include "fileio.h"
#include "macro.h"
+#include "util.h"
static bool test_async = false;
diff --git a/src/test/test-boot-timestamps.c b/src/test/test-boot-timestamps.c
index 06d93af533..fab33d20c7 100644
--- a/src/test/test-boot-timestamps.c
+++ b/src/test/test-boot-timestamps.c
@@ -20,11 +20,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
-#include "log.h"
+#include "acpi-fpdt.h"
#include "boot-timestamps.h"
#include "efivars.h"
-#include "acpi-fpdt.h"
+#include "log.h"
+#include "util.h"
static int test_acpi_fpdt(void) {
usec_t loader_start;
diff --git a/src/test/test-btrfs.c b/src/test/test-btrfs.c
index e4771c9dd7..33356f8387 100644
--- a/src/test/test-btrfs.c
+++ b/src/test/test-btrfs.c
@@ -21,23 +21,26 @@
#include <fcntl.h>
-#include "log.h"
+#include "btrfs-util.h"
+#include "fd-util.h"
#include "fileio.h"
+#include "log.h"
+#include "parse-util.h"
+#include "string-util.h"
#include "util.h"
-#include "btrfs-util.h"
int main(int argc, char *argv[]) {
+ BtrfsQuotaInfo quota;
int r, fd;
fd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
if (fd < 0)
log_error_errno(errno, "Failed to open root directory: %m");
else {
- BtrfsSubvolInfo info;
- BtrfsQuotaInfo quota;
char ts[FORMAT_TIMESTAMP_MAX], bs[FORMAT_BYTES_MAX];
+ BtrfsSubvolInfo info;
- r = btrfs_subvol_get_info_fd(fd, &info);
+ r = btrfs_subvol_get_info_fd(fd, 0, &info);
if (r < 0)
log_error_errno(r, "Failed to get subvolume info: %m");
else {
@@ -45,7 +48,7 @@ int main(int argc, char *argv[]) {
log_info("read-only (search): %s", yes_no(info.read_only));
}
- r = btrfs_subvol_get_quota_fd(fd, &quota);
+ r = btrfs_qgroup_get_quota_fd(fd, 0, &quota);
if (r < 0)
log_error_errno(r, "Failed to get quota info: %m");
else {
@@ -80,15 +83,15 @@ int main(int argc, char *argv[]) {
if (r < 0)
log_error_errno(r, "Failed to make snapshot: %m");
- r = btrfs_subvol_remove("/xxxtest", false);
+ r = btrfs_subvol_remove("/xxxtest", BTRFS_REMOVE_QUOTA);
if (r < 0)
log_error_errno(r, "Failed to remove subvolume: %m");
- r = btrfs_subvol_remove("/xxxtest2", false);
+ r = btrfs_subvol_remove("/xxxtest2", BTRFS_REMOVE_QUOTA);
if (r < 0)
log_error_errno(r, "Failed to remove subvolume: %m");
- r = btrfs_subvol_remove("/xxxtest3", false);
+ r = btrfs_subvol_remove("/xxxtest3", BTRFS_REMOVE_QUOTA);
if (r < 0)
log_error_errno(r, "Failed to remove subvolume: %m");
@@ -96,7 +99,7 @@ int main(int argc, char *argv[]) {
if (r < 0)
log_error_errno(r, "Failed to make snapshot: %m");
- r = btrfs_subvol_remove("/etc2", false);
+ r = btrfs_subvol_remove("/etc2", BTRFS_REMOVE_QUOTA);
if (r < 0)
log_error_errno(r, "Failed to remove subvolume: %m");
@@ -137,13 +140,61 @@ int main(int argc, char *argv[]) {
if (r < 0)
log_error_errno(r, "Failed to snapshot subvolume: %m");
- r = btrfs_subvol_remove("/xxxrectest", true);
+ r = btrfs_subvol_remove("/xxxrectest", BTRFS_REMOVE_QUOTA|BTRFS_REMOVE_RECURSIVE);
if (r < 0)
log_error_errno(r, "Failed to recursively remove subvolume: %m");
- r = btrfs_subvol_remove("/xxxrectest2", true);
+ r = btrfs_subvol_remove("/xxxrectest2", BTRFS_REMOVE_QUOTA|BTRFS_REMOVE_RECURSIVE);
if (r < 0)
log_error_errno(r, "Failed to recursively remove subvolume: %m");
+ r = btrfs_subvol_make("/xxxquotatest");
+ if (r < 0)
+ log_error_errno(r, "Failed to make subvolume: %m");
+
+ r = btrfs_subvol_auto_qgroup("/xxxquotatest", 0, true);
+ if (r < 0)
+ log_error_errno(r, "Failed to set up auto qgroup: %m");
+
+ r = btrfs_subvol_make("/xxxquotatest/beneath");
+ if (r < 0)
+ log_error_errno(r, "Failed to make subvolume: %m");
+
+ r = btrfs_subvol_auto_qgroup("/xxxquotatest/beneath", 0, false);
+ if (r < 0)
+ log_error_errno(r, "Failed to set up auto qgroup: %m");
+
+ r = btrfs_qgroup_set_limit("/xxxquotatest/beneath", 0, 4ULL * 1024 * 1024 * 1024);
+ if (r < 0)
+ log_error_errno(r, "Failed to set up quota limit: %m");
+
+ r = btrfs_subvol_set_subtree_quota_limit("/xxxquotatest", 0, 5ULL * 1024 * 1024 * 1024);
+ if (r < 0)
+ log_error_errno(r, "Failed to set up quota limit: %m");
+
+ r = btrfs_subvol_snapshot("/xxxquotatest", "/xxxquotatest2", BTRFS_SNAPSHOT_RECURSIVE|BTRFS_SNAPSHOT_QUOTA);
+ if (r < 0)
+ log_error_errno(r, "Failed to setup snapshot: %m");
+
+ r = btrfs_qgroup_get_quota("/xxxquotatest2/beneath", 0, &quota);
+ if (r < 0)
+ log_error_errno(r, "Failed to query quota: %m");
+
+ assert_se(quota.referenced_max == 4ULL * 1024 * 1024 * 1024);
+
+ r = btrfs_subvol_get_subtree_quota("/xxxquotatest2", 0, &quota);
+ if (r < 0)
+ log_error_errno(r, "Failed to query quota: %m");
+
+ assert_se(quota.referenced_max == 5ULL * 1024 * 1024 * 1024);
+
+ r = btrfs_subvol_remove("/xxxquotatest", BTRFS_REMOVE_QUOTA|BTRFS_REMOVE_RECURSIVE);
+ if (r < 0)
+ log_error_errno(r, "Failed remove subvolume: %m");
+
+ r = btrfs_subvol_remove("/xxxquotatest2", BTRFS_REMOVE_QUOTA|BTRFS_REMOVE_RECURSIVE);
+ if (r < 0)
+ log_error_errno(r, "Failed remove subvolume: %m");
+
return 0;
}
diff --git a/src/test/test-calendarspec.c b/src/test/test-calendarspec.c
index 87e1da1258..9cef7154c6 100644
--- a/src/test/test-calendarspec.c
+++ b/src/test/test-calendarspec.c
@@ -21,7 +21,9 @@
#include <string.h>
+#include "alloc-util.h"
#include "calendarspec.h"
+#include "string-util.h"
#include "util.h"
static void test_one(const char *input, const char *output) {
@@ -50,6 +52,44 @@ static void test_one(const char *input, const char *output) {
assert_se(streq(q, p));
}
+static void test_next(const char *input, const char *new_tz, usec_t after, usec_t expect) {
+ CalendarSpec *c;
+ usec_t u;
+ char *old_tz;
+ char buf[FORMAT_TIMESTAMP_MAX];
+ int r;
+
+ old_tz = getenv("TZ");
+ if (old_tz)
+ old_tz = strdupa(old_tz);
+
+ if (new_tz)
+ assert_se(setenv("TZ", new_tz, 1) >= 0);
+ else
+ assert_se(unsetenv("TZ") >= 0);
+ tzset();
+
+ assert_se(calendar_spec_from_string(input, &c) >= 0);
+
+ printf("\"%s\"\n", input);
+
+ u = after;
+ r = calendar_spec_next_usec(c, after, &u);
+ printf("At: %s\n", r < 0 ? strerror(-r) : format_timestamp_us(buf, sizeof(buf), u));
+ if (expect != (usec_t)-1)
+ assert_se(r >= 0 && u == expect);
+ else
+ assert(r == -ENOENT);
+
+ calendar_spec_free(c);
+
+ if (old_tz)
+ assert_se(setenv("TZ", old_tz, 1) >= 0);
+ else
+ assert_se(unsetenv("TZ") >= 0);
+ tzset();
+}
+
int main(int argc, char* argv[]) {
CalendarSpec *c;
@@ -82,11 +122,31 @@ int main(int argc, char* argv[]) {
test_one("semi-annually", "*-01,07-01 00:00:00");
test_one("annually", "*-01-01 00:00:00");
test_one("*:2/3", "*-*-* *:02/3:00");
+ test_one("2015-10-25 01:00:00 uTc", "2015-10-25 01:00:00 UTC");
+ test_one("2016-03-27 03:17:00.4200005", "2016-03-27 03:17:00.420001");
+ test_one("2016-03-27 03:17:00/0.42", "2016-03-27 03:17:00/0.420000");
+ test_one("2016-03-27 03:17:00/0.42", "2016-03-27 03:17:00/0.420000");
+
+ test_next("2016-03-27 03:17:00", "", 12345, 1459048620000000);
+ test_next("2016-03-27 03:17:00", "CET", 12345, 1459041420000000);
+ test_next("2016-03-27 03:17:00", "EET", 12345, -1);
+ test_next("2016-03-27 03:17:00 UTC", NULL, 12345, 1459048620000000);
+ test_next("2016-03-27 03:17:00 UTC", "", 12345, 1459048620000000);
+ test_next("2016-03-27 03:17:00 UTC", "CET", 12345, 1459048620000000);
+ test_next("2016-03-27 03:17:00 UTC", "EET", 12345, 1459048620000000);
+ test_next("2016-03-27 03:17:00.420000001 UTC", "EET", 12345, 1459048620420000);
+ test_next("2016-03-27 03:17:00.4200005 UTC", "EET", 12345, 1459048620420001);
+ test_next("2015-11-13 09:11:23.42", "EET", 12345, 1447398683420000);
+ test_next("2015-11-13 09:11:23.42/1.77", "EET", 1447398683420000, 1447398685190000);
+ test_next("2015-11-13 09:11:23.42/1.77", "EET", 1447398683419999, 1447398683420000);
assert_se(calendar_spec_from_string("test", &c) < 0);
assert_se(calendar_spec_from_string("", &c) < 0);
assert_se(calendar_spec_from_string("7", &c) < 0);
assert_se(calendar_spec_from_string("121212:1:2", &c) < 0);
+ assert_se(calendar_spec_from_string("2000-03-05.23 00:00:00", &c) < 0);
+ assert_se(calendar_spec_from_string("2000-03-05 00:00.1:00", &c) < 0);
+ assert_se(calendar_spec_from_string("00:00:00/0.00000001", &c) < 0);
return 0;
}
diff --git a/src/test/test-cap-list.c b/src/test/test-cap-list.c
index 43a2d35b80..4418bafda6 100644
--- a/src/test/test-cap-list.c
+++ b/src/test/test-cap-list.c
@@ -19,12 +19,15 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
-#include "fileio.h"
-#include "cap-list.h"
-#include "capability.h"
#include <sys/prctl.h>
+#include "alloc-util.h"
+#include "cap-list.h"
+#include "capability-util.h"
+#include "fileio.h"
+#include "parse-util.h"
+#include "util.h"
+
/* verify the capability parser */
static void test_cap_list(void) {
int i;
diff --git a/src/test/test-capability.c b/src/test/test-capability.c
index f47452ce72..629bb63c81 100644
--- a/src/test/test-capability.c
+++ b/src/test/test-capability.c
@@ -17,20 +17,23 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/wait.h>
-#include <sys/capability.h>
-#include <sys/socket.h>
#include <netinet/in.h>
#include <pwd.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
#include <unistd.h>
-#include "capability.h"
-#include "util.h"
+#include "capability-util.h"
+#include "fd-util.h"
#include "macro.h"
+#include "util.h"
static uid_t test_uid = -1;
static gid_t test_gid = -1;
-// We keep CAP_DAC_OVERRIDE to avoid errors with gcov when doing test coverage
+
+/* We keep CAP_DAC_OVERRIDE to avoid errors with gcov when doing test coverage */
static uint64_t test_flags = 1ULL << CAP_DAC_OVERRIDE;
static void fork_test(void (*test_func)(void)) {
@@ -64,8 +67,9 @@ static void show_capabilities(void) {
cap_free(text);
}
-static int setup_tests(void) {
+static int setup_tests(bool *run_ambient) {
struct passwd *nobody;
+ int r;
nobody = getpwnam("nobody");
if (!nobody) {
@@ -75,6 +79,18 @@ static int setup_tests(void) {
test_uid = nobody->pw_uid;
test_gid = nobody->pw_gid;
+ *run_ambient = false;
+
+ r = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0);
+
+ /* There's support for PR_CAP_AMBIENT if the prctl() call
+ * succeeded or error code was something else than EINVAL. The
+ * EINVAL check should be good enough to rule out false
+ * positives. */
+
+ if (r >= 0 || errno != EINVAL)
+ *run_ambient = true;
+
return 0;
}
@@ -138,8 +154,53 @@ static void test_have_effective_cap(void) {
assert_se(!have_effective_cap(CAP_CHOWN));
}
+static void test_update_inherited_set(void) {
+ cap_t caps;
+ uint64_t set = 0;
+ cap_flag_value_t fv;
+
+ caps = cap_get_proc();
+ assert_se(caps);
+ assert_se(!cap_get_flag(caps, CAP_CHOWN, CAP_INHERITABLE, &fv));
+ assert(fv == CAP_CLEAR);
+
+ set = (UINT64_C(1) << CAP_CHOWN);
+
+ assert_se(!capability_update_inherited_set(caps, set));
+ assert_se(!cap_get_flag(caps, CAP_CHOWN, CAP_INHERITABLE, &fv));
+ assert(fv == CAP_SET);
+
+ cap_free(caps);
+}
+
+static void test_set_ambient_caps(void) {
+ cap_t caps;
+ uint64_t set = 0;
+ cap_flag_value_t fv;
+
+ caps = cap_get_proc();
+ assert_se(caps);
+ assert_se(!cap_get_flag(caps, CAP_CHOWN, CAP_INHERITABLE, &fv));
+ assert(fv == CAP_CLEAR);
+ cap_free(caps);
+
+ assert_se(prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) == 0);
+
+ set = (UINT64_C(1) << CAP_CHOWN);
+
+ assert_se(!capability_ambient_set_apply(set, true));
+
+ caps = cap_get_proc();
+ assert_se(!cap_get_flag(caps, CAP_CHOWN, CAP_INHERITABLE, &fv));
+ assert(fv == CAP_SET);
+ cap_free(caps);
+
+ assert_se(prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) == 1);
+}
+
int main(int argc, char *argv[]) {
int r;
+ bool run_ambient;
log_parse_environment();
log_open();
@@ -147,14 +208,19 @@ int main(int argc, char *argv[]) {
if (getuid() != 0)
return EXIT_TEST_SKIP;
- r = setup_tests();
+ r = setup_tests(&run_ambient);
if (r < 0)
return -r;
show_capabilities();
test_drop_privileges();
+ test_update_inherited_set();
+
fork_test(test_have_effective_cap);
+ if (run_ambient)
+ fork_test(test_set_ambient_caps);
+
return 0;
}
diff --git a/src/test/test-cgroup-mask.c b/src/test/test-cgroup-mask.c
index de6c421b82..a33519b9da 100644
--- a/src/test/test-cgroup-mask.c
+++ b/src/test/test-cgroup-mask.c
@@ -21,10 +21,10 @@
#include <stdio.h>
-#include "manager.h"
-#include "unit.h"
#include "macro.h"
+#include "manager.h"
#include "test-helper.h"
+#include "unit.h"
static int test_cgroup_mask(void) {
Manager *m = NULL;
@@ -41,6 +41,17 @@ static int test_cgroup_mask(void) {
return EXIT_TEST_SKIP;
}
assert_se(r >= 0);
+
+ /* Turn off all kinds of default accouning, so that we can
+ * verify the masks resulting of our configuration and nothing
+ * else. */
+ m->default_cpu_accounting =
+ m->default_memory_accounting =
+ m->default_blockio_accounting =
+ m->default_tasks_accounting = false;
+ m->default_tasks_max = (uint64_t) -1;
+
+ assert_se(r >= 0);
assert_se(manager_startup(m, serial, fdset) >= 0);
/* Load units and verify hierarchy. */
diff --git a/src/test/test-cgroup-util.c b/src/test/test-cgroup-util.c
index 4ecf09a29e..a48b324e26 100644
--- a/src/test/test-cgroup-util.c
+++ b/src/test/test-cgroup-util.c
@@ -19,12 +19,17 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-
-#include "util.h"
+#include "alloc-util.h"
#include "cgroup-util.h"
-#include "test-helper.h"
+#include "dirent-util.h"
+#include "fd-util.h"
#include "formats-util.h"
+#include "parse-util.h"
#include "process-util.h"
+#include "string-util.h"
+#include "test-helper.h"
+#include "user-util.h"
+#include "util.h"
static void check_p_d_u(const char *path, int code, const char *result) {
_cleanup_free_ char *unit = NULL;
diff --git a/src/test/test-cgroup.c b/src/test/test-cgroup.c
index 37b1c3554a..c20a29ba1f 100644
--- a/src/test/test-cgroup.c
+++ b/src/test/test-cgroup.c
@@ -19,11 +19,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
#include <string.h>
+#include <unistd.h>
#include "cgroup-util.h"
#include "path-util.h"
+#include "string-util.h"
#include "util.h"
int main(int argc, char*argv[]) {
diff --git a/src/test/test-condition.c b/src/test/test-condition.c
index b788c9532d..8903d10db7 100644
--- a/src/test/test-condition.c
+++ b/src/test/test-condition.c
@@ -17,18 +17,20 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "sd-id128.h"
+
+#include "alloc-util.h"
+#include "apparmor-util.h"
+#include "architecture.h"
+#include "audit-util.h"
#include "condition.h"
-#include "macro.h"
-#include "util.h"
+#include "hostname-util.h"
+#include "ima-util.h"
#include "log.h"
-#include "architecture.h"
-#include "sd-id128.h"
+#include "macro.h"
#include "selinux-util.h"
-#include "audit.h"
-#include "ima-util.h"
-#include "apparmor-util.h"
#include "smack-util.h"
-#include "hostname-util.h"
+#include "util.h"
static void test_condition_test_path(void) {
Condition *condition;
@@ -201,7 +203,7 @@ static void test_condition_test_security(void) {
condition_free(condition);
condition = condition_new(CONDITION_SECURITY, "selinux", false, true);
- assert_se(condition_test(condition) != mac_selinux_use());
+ assert_se(condition_test(condition) != mac_selinux_have());
condition_free(condition);
condition = condition_new(CONDITION_SECURITY, "ima", false, false);
diff --git a/src/test/test-conf-files.c b/src/test/test-conf-files.c
index 01ece022c1..86ac513d4f 100644
--- a/src/test/test-conf-files.c
+++ b/src/test/test-conf-files.c
@@ -19,14 +19,19 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
#include <stdarg.h>
+#include <stdio.h>
+#include "alloc-util.h"
#include "conf-files.h"
+#include "fs-util.h"
#include "macro.h"
+#include "parse-util.h"
+#include "rm-rf.h"
+#include "string-util.h"
#include "strv.h"
+#include "user-util.h"
#include "util.h"
-#include "rm-rf.h"
static void setup_test_dir(char *tmp_dir, const char *files, ...) {
va_list ap;
@@ -36,7 +41,7 @@ static void setup_test_dir(char *tmp_dir, const char *files, ...) {
va_start(ap, files);
while (files != NULL) {
_cleanup_free_ char *path = strappend(tmp_dir, files);
- assert_se(touch_file(path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0) == 0);
+ assert_se(touch_file(path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID) == 0);
files = va_arg(ap, const char *);
}
va_end(ap);
diff --git a/src/test/test-conf-parser.c b/src/test/test-conf-parser.c
index 463906d304..b3a4c40339 100644
--- a/src/test/test-conf-parser.c
+++ b/src/test/test-conf-parser.c
@@ -18,10 +18,11 @@
***/
#include "conf-parser.h"
+#include "log.h"
#include "macro.h"
-#include "util.h"
+#include "string-util.h"
#include "strv.h"
-#include "log.h"
+#include "util.h"
static void test_config_parse_path_one(const char *rvalue, const char *expected) {
char *path = NULL;
diff --git a/src/test/test-copy.c b/src/test/test-copy.c
index a03a68bd43..ad57cb0202 100644
--- a/src/test/test-copy.c
+++ b/src/test/test-copy.c
@@ -19,14 +19,18 @@
#include <unistd.h>
+#include "alloc-util.h"
#include "copy.h"
-#include "path-util.h"
+#include "fd-util.h"
#include "fileio.h"
+#include "fs-util.h"
+#include "macro.h"
#include "mkdir.h"
+#include "path-util.h"
+#include "rm-rf.h"
+#include "string-util.h"
#include "strv.h"
-#include "macro.h"
#include "util.h"
-#include "rm-rf.h"
static void test_copy_file(void) {
_cleanup_free_ char *buf = NULL;
diff --git a/src/test/test-daemon.c b/src/test/test-daemon.c
index 7e0ac754d1..45fb554445 100644
--- a/src/test/test-daemon.c
+++ b/src/test/test-daemon.c
@@ -21,9 +21,22 @@
#include <unistd.h>
-#include "systemd/sd-daemon.h"
+#include "sd-daemon.h"
+
+#include "strv.h"
int main(int argc, char*argv[]) {
+ _cleanup_strv_free_ char **l = NULL;
+ int n, i;
+
+ n = sd_listen_fds_with_names(false, &l);
+ if (n < 0) {
+ log_error_errno(n, "Failed to get listening fds: %m");
+ return EXIT_FAILURE;
+ }
+
+ for (i = 0; i < n; i++)
+ log_info("fd=%i name=%s\n", SD_LISTEN_FDS_START + i, l[i]);
sd_notify(0,
"STATUS=Starting up");
@@ -49,5 +62,5 @@ int main(int argc, char*argv[]) {
"STOPPING=1");
sleep(5);
- return 0;
+ return EXIT_SUCCESS;
}
diff --git a/src/test/test-date.c b/src/test/test-date.c
index 00b569080c..851d384117 100644
--- a/src/test/test-date.c
+++ b/src/test/test-date.c
@@ -21,18 +21,22 @@
#include <string.h>
+#include "alloc-util.h"
+#include "string-util.h"
#include "util.h"
-static void test_one(const char *p) {
+static void test_should_pass(const char *p) {
usec_t t, q;
- char buf[FORMAT_TIMESTAMP_MAX], buf_relative[FORMAT_TIMESTAMP_RELATIVE_MAX];
+ char buf[FORMAT_TIMESTAMP_MAX], buf_relative[FORMAT_TIMESTAMP_RELATIVE_MAX], *sp;
assert_se(parse_timestamp(p, &t) >= 0);
- format_timestamp(buf, sizeof(buf), t);
+ format_timestamp_us(buf, sizeof(buf), t);
log_info("%s", buf);
/* Chop off timezone */
- *strrchr(buf, ' ') = 0;
+ sp = strrchr(buf, ' ');
+ assert_se(sp);
+ *sp = 0;
assert_se(parse_timestamp(buf, &q) >= 0);
assert_se(q == t);
@@ -42,23 +46,57 @@ static void test_one(const char *p) {
assert_se(parse_timestamp(buf, &q) >= 0);
}
+static void test_should_parse(const char *p) {
+ usec_t t;
+
+ assert_se(parse_timestamp(p, &t) >= 0);
+}
+
+static void test_should_fail(const char *p) {
+ usec_t t;
+
+ assert_se(parse_timestamp(p, &t) < 0);
+}
+
+static void test_one(const char *p) {
+ _cleanup_free_ char *with_utc;
+
+ log_info("Test: %s", p);
+ with_utc = strjoin(p, " UTC", NULL);
+ test_should_pass(p);
+ test_should_pass(with_utc);
+}
+
+static void test_one_noutc(const char *p) {
+ _cleanup_free_ char *with_utc;
+
+ log_info("Test: %s", p);
+ with_utc = strjoin(p, " UTC", NULL);
+ test_should_pass(p);
+ test_should_fail(with_utc);
+}
+
int main(int argc, char *argv[]) {
test_one("17:41");
test_one("18:42:44");
+ test_one("18:42:44.0");
+ test_one("18:42:44.999999999999");
test_one("12-10-02 12:13:14");
test_one("12-10-2 12:13:14");
test_one("12-10-03 12:13");
test_one("2012-12-30 18:42");
test_one("2012-10-02");
test_one("Tue 2012-10-02");
- test_one("now");
+ test_one_noutc("now");
test_one("yesterday");
test_one("today");
test_one("tomorrow");
- test_one("+2d");
- test_one("+2y 4d");
- test_one("5months ago");
- test_one("@1395716396");
+ test_one_noutc("+2d");
+ test_one_noutc("+2y 4d");
+ test_one_noutc("5months ago");
+ test_one_noutc("@1395716396");
+ test_should_parse("today UTC");
+ test_should_fail("today UTC UTC");
return 0;
}
diff --git a/src/test/test-device-nodes.c b/src/test/test-device-nodes.c
index 59ba4be087..646b168cc0 100644
--- a/src/test/test-device-nodes.c
+++ b/src/test/test-device-nodes.c
@@ -21,7 +21,9 @@
#include <sys/types.h>
+#include "alloc-util.h"
#include "device-nodes.h"
+#include "string-util.h"
#include "util.h"
/* helpers for test_encode_devnode_name */
diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c
index 2193eb6f7d..3b260ee75d 100644
--- a/src/test/test-dns-domain.c
+++ b/src/test/test-dns-domain.c
@@ -19,8 +19,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "macro.h"
+#include "alloc-util.h"
#include "dns-domain.h"
+#include "macro.h"
+#include "string-util.h"
static void test_dns_label_unescape_one(const char *what, const char *expect, size_t buffer_sz, int ret) {
char buffer[buffer_sz];
@@ -37,7 +39,7 @@ static void test_dns_label_unescape_one(const char *what, const char *expect, si
static void test_dns_label_unescape(void) {
test_dns_label_unescape_one("hallo", "hallo", 6, 5);
- test_dns_label_unescape_one("hallo", "hallo", 4, -ENOSPC);
+ test_dns_label_unescape_one("hallo", "hallo", 4, -ENOBUFS);
test_dns_label_unescape_one("", "", 10, 0);
test_dns_label_unescape_one("hallo\\.foobar", "hallo.foobar", 20, 12);
test_dns_label_unescape_one("hallo.foobar", "hallo", 10, 5);
@@ -50,6 +52,66 @@ static void test_dns_label_unescape(void) {
test_dns_label_unescape_one("foobar.", "foobar", 20, 6);
}
+static void test_dns_name_to_wire_format_one(const char *what, const char *expect, size_t buffer_sz, int ret) {
+ uint8_t buffer[buffer_sz];
+ int r;
+
+ r = dns_name_to_wire_format(what, buffer, buffer_sz, false);
+ assert_se(r == ret);
+
+ if (r < 0)
+ return;
+
+ assert_se(!memcmp(buffer, expect, r));
+}
+
+static void test_dns_name_to_wire_format(void) {
+ static const char out0[] = { 0 };
+ static const char out1[] = { 3, 'f', 'o', 'o', 0 };
+ static const char out2[] = { 5, 'h', 'a', 'l', 'l', 'o', 3, 'f', 'o', 'o', 3, 'b', 'a', 'r', 0 };
+ static const char out3[] = { 4, ' ', 'f', 'o', 'o', 3, 'b', 'a', 'r', 0 };
+ static const char out4[] = { 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
+ 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
+ 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
+ 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
+ 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
+ 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
+ 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
+ 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
+ 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
+ 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
+ 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
+ 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
+ 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
+ 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
+ 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
+ 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
+ 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
+ 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
+ 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
+ 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
+ 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
+ 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
+ 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
+ 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
+ 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
+ 3, 'a', '1', '2', 0 };
+
+ test_dns_name_to_wire_format_one("", out0, sizeof(out0), sizeof(out0));
+
+ test_dns_name_to_wire_format_one("foo", out1, sizeof(out1), sizeof(out1));
+ test_dns_name_to_wire_format_one("foo", out1, sizeof(out1) + 1, sizeof(out1));
+ test_dns_name_to_wire_format_one("foo", out1, sizeof(out1) - 1, -ENOBUFS);
+
+ test_dns_name_to_wire_format_one("hallo.foo.bar", out2, sizeof(out2), sizeof(out2));
+ test_dns_name_to_wire_format_one("hallo.foo..bar", NULL, 32, -EINVAL);
+
+ test_dns_name_to_wire_format_one("\\032foo.bar", out3, sizeof(out3), sizeof(out3));
+
+ test_dns_name_to_wire_format_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a123", NULL, 500, -EINVAL);
+ test_dns_name_to_wire_format_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12", out4, sizeof(out4), sizeof(out4));
+}
+
static void test_dns_label_unescape_suffix_one(const char *what, const char *expect1, const char *expect2, size_t buffer_sz, int ret1, int ret2) {
char buffer[buffer_sz];
const char *label;
@@ -70,7 +132,7 @@ static void test_dns_label_unescape_suffix_one(const char *what, const char *exp
static void test_dns_label_unescape_suffix(void) {
test_dns_label_unescape_suffix_one("hallo", "hallo", "", 6, 5, 0);
- test_dns_label_unescape_suffix_one("hallo", "hallo", "", 4, -ENOSPC, -ENOSPC);
+ test_dns_label_unescape_suffix_one("hallo", "hallo", "", 4, -ENOBUFS, -ENOBUFS);
test_dns_label_unescape_suffix_one("", "", "", 10, 0, 0);
test_dns_label_unescape_suffix_one("hallo\\.foobar", "hallo.foobar", "", 20, 12, 0);
test_dns_label_unescape_suffix_one("hallo.foobar", "foobar", "hallo", 10, 6, 5);
@@ -78,9 +140,9 @@ static void test_dns_label_unescape_suffix(void) {
test_dns_label_unescape_suffix_one("hallo\\", "hallo", "hallo", 20, -EINVAL, -EINVAL);
test_dns_label_unescape_suffix_one("hallo\\032 ", "hallo ", "", 20, 7, 0);
test_dns_label_unescape_suffix_one(".", "", "", 20, 0, 0);
- test_dns_label_unescape_suffix_one("..", "", "", 20, 0, 0);
+ test_dns_label_unescape_suffix_one("..", "", "", 20, 0, -EINVAL);
test_dns_label_unescape_suffix_one(".foobar", "foobar", "", 20, 6, -EINVAL);
- test_dns_label_unescape_suffix_one("foobar.", "", "foobar", 20, 0, 6);
+ test_dns_label_unescape_suffix_one("foobar.", "foobar", "", 20, 6, 0);
test_dns_label_unescape_suffix_one("foo\\\\bar", "foo\\bar", "", 20, 7, 0);
test_dns_label_unescape_suffix_one("foo.bar", "bar", "foo", 20, 3, 3);
test_dns_label_unescape_suffix_one("foo..bar", "bar", "", 20, 3, -EINVAL);
@@ -94,7 +156,7 @@ static void test_dns_label_escape_one(const char *what, size_t l, const char *ex
_cleanup_free_ char *t = NULL;
int r;
- r = dns_label_escape(what, l, &t);
+ r = dns_label_escape_new(what, l, &t);
assert_se(r == ret);
if (r < 0)
@@ -104,9 +166,9 @@ static void test_dns_label_escape_one(const char *what, size_t l, const char *ex
}
static void test_dns_label_escape(void) {
- test_dns_label_escape_one("", 0, "", 0);
+ test_dns_label_escape_one("", 0, NULL, -EINVAL);
test_dns_label_escape_one("hallo", 5, "hallo", 5);
- test_dns_label_escape_one("hallo", 6, NULL, -EINVAL);
+ test_dns_label_escape_one("hallo", 6, "hallo\\000", 9);
test_dns_label_escape_one("hallo hallo.foobar,waldi", 24, "hallo\\032hallo\\.foobar\\044waldi", 31);
}
@@ -128,7 +190,7 @@ static void test_dns_name_normalize(void) {
test_dns_name_normalize_one("f", "f", 0);
test_dns_name_normalize_one("f.waldi", "f.waldi", 0);
test_dns_name_normalize_one("f \\032.waldi", "f\\032\\032.waldi", 0);
- test_dns_name_normalize_one("\\000", NULL, -EINVAL);
+ test_dns_name_normalize_one("\\000", "\\000", 0);
test_dns_name_normalize_one("..", NULL, -EINVAL);
test_dns_name_normalize_one(".foobar", NULL, -EINVAL);
test_dns_name_normalize_one("foobar.", "foobar", 0);
@@ -154,7 +216,7 @@ static void test_dns_name_equal(void) {
test_dns_name_equal_one("abc.def", "CBA.def", false);
test_dns_name_equal_one("", "xxx", false);
test_dns_name_equal_one("ab", "a", false);
- test_dns_name_equal_one("\\000", "xxxx", -EINVAL);
+ test_dns_name_equal_one("\\000", "\\000", true);
test_dns_name_equal_one(".", "", true);
test_dns_name_equal_one(".", ".", true);
test_dns_name_equal_one("..", "..", -EINVAL);
@@ -214,21 +276,40 @@ static void test_dns_name_endswith(void) {
test_dns_name_endswith_one("x.y\001.z", "waldo", -EINVAL);
}
-static void test_dns_name_root(void) {
- assert_se(dns_name_root("") == true);
- assert_se(dns_name_root(".") == true);
- assert_se(dns_name_root("xxx") == false);
- assert_se(dns_name_root("xxx.") == false);
- assert_se(dns_name_root("..") == -EINVAL);
+static void test_dns_name_startswith_one(const char *a, const char *b, int ret) {
+ assert_se(dns_name_startswith(a, b) == ret);
+}
+
+static void test_dns_name_startswith(void) {
+ test_dns_name_startswith_one("", "", true);
+ test_dns_name_startswith_one("", "xxx", false);
+ test_dns_name_startswith_one("xxx", "", true);
+ test_dns_name_startswith_one("x", "x", true);
+ test_dns_name_startswith_one("x", "y", false);
+ test_dns_name_startswith_one("x.y", "x.y", true);
+ test_dns_name_startswith_one("x.y", "y.x", false);
+ test_dns_name_startswith_one("x.y", "x", true);
+ test_dns_name_startswith_one("x.y", "X", true);
+ test_dns_name_startswith_one("x.y", "y", false);
+ test_dns_name_startswith_one("x.y", "", true);
+ test_dns_name_startswith_one("x.y", "X", true);
}
-static void test_dns_name_single_label(void) {
- assert_se(dns_name_single_label("") == false);
- assert_se(dns_name_single_label(".") == false);
- assert_se(dns_name_single_label("..") == -EINVAL);
- assert_se(dns_name_single_label("x") == true);
- assert_se(dns_name_single_label("x.") == true);
- assert_se(dns_name_single_label("xx.yy") == false);
+static void test_dns_name_is_root(void) {
+ assert_se(dns_name_is_root(""));
+ assert_se(dns_name_is_root("."));
+ assert_se(!dns_name_is_root("xxx"));
+ assert_se(!dns_name_is_root("xxx."));
+ assert_se(!dns_name_is_root(".."));
+}
+
+static void test_dns_name_is_single_label(void) {
+ assert_se(!dns_name_is_single_label(""));
+ assert_se(!dns_name_is_single_label("."));
+ assert_se(!dns_name_is_single_label(".."));
+ assert_se(dns_name_is_single_label("x"));
+ assert_se(dns_name_is_single_label("x."));
+ assert_se(!dns_name_is_single_label("xx.yy"));
}
static void test_dns_name_reverse_one(const char *address, const char *name) {
@@ -282,6 +363,259 @@ static void test_dns_name_is_valid(void) {
test_dns_name_is_valid_one("\\zbar", 0);
test_dns_name_is_valid_one("ä", 1);
test_dns_name_is_valid_one("\n", 0);
+
+ /* 256 characters*/
+ test_dns_name_is_valid_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345", 0);
+
+ /* 255 characters*/
+ test_dns_name_is_valid_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a1234", 0);
+
+ /* 254 characters*/
+ test_dns_name_is_valid_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a123", 0);
+
+ /* 253 characters*/
+ test_dns_name_is_valid_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12", 1);
+
+ /* label of 64 chars length */
+ test_dns_name_is_valid_one("a123456789a123456789a123456789a123456789a123456789a123456789a123", 0);
+
+ /* label of 63 chars length */
+ test_dns_name_is_valid_one("a123456789a123456789a123456789a123456789a123456789a123456789a12", 1);
+}
+
+static void test_dns_service_name_is_valid(void) {
+ assert_se(dns_service_name_is_valid("Lennart's Compüter"));
+ assert_se(dns_service_name_is_valid("piff.paff"));
+
+ assert_se(!dns_service_name_is_valid(NULL));
+ assert_se(!dns_service_name_is_valid(""));
+ assert_se(!dns_service_name_is_valid("foo\nbar"));
+ assert_se(!dns_service_name_is_valid("foo\201bar"));
+ assert_se(!dns_service_name_is_valid("this is an overly long string that is certainly longer than 63 characters"));
+}
+
+static void test_dns_srv_type_is_valid(void) {
+
+ assert_se(dns_srv_type_is_valid("_http._tcp"));
+ assert_se(dns_srv_type_is_valid("_foo-bar._tcp"));
+ assert_se(dns_srv_type_is_valid("_w._udp"));
+ assert_se(dns_srv_type_is_valid("_a800._tcp"));
+ assert_se(dns_srv_type_is_valid("_a-800._tcp"));
+
+ assert_se(!dns_srv_type_is_valid(NULL));
+ assert_se(!dns_srv_type_is_valid(""));
+ assert_se(!dns_srv_type_is_valid("x"));
+ assert_se(!dns_srv_type_is_valid("_foo"));
+ assert_se(!dns_srv_type_is_valid("_tcp"));
+ assert_se(!dns_srv_type_is_valid("_"));
+ assert_se(!dns_srv_type_is_valid("_foo."));
+ assert_se(!dns_srv_type_is_valid("_föo._tcp"));
+ assert_se(!dns_srv_type_is_valid("_f\no._tcp"));
+ assert_se(!dns_srv_type_is_valid("_800._tcp"));
+ assert_se(!dns_srv_type_is_valid("_-800._tcp"));
+ assert_se(!dns_srv_type_is_valid("_-foo._tcp"));
+ assert_se(!dns_srv_type_is_valid("_piep._foo._udp"));
+}
+
+static void test_dns_service_join_one(const char *a, const char *b, const char *c, int r, const char *d) {
+ _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL, *t = NULL;
+
+ assert_se(dns_service_join(a, b, c, &t) == r);
+ assert_se(streq_ptr(t, d));
+
+ if (r < 0)
+ return;
+
+ assert_se(dns_service_split(t, &x, &y, &z) >= 0);
+ assert_se(streq_ptr(a, x));
+ assert_se(streq_ptr(b, y));
+ assert_se(streq_ptr(c, z));
+}
+
+static void test_dns_service_join(void) {
+ test_dns_service_join_one("", "", "", -EINVAL, NULL);
+ test_dns_service_join_one("", "_http._tcp", "", -EINVAL, NULL);
+ test_dns_service_join_one("", "_http._tcp", "foo", -EINVAL, NULL);
+ test_dns_service_join_one("foo", "", "foo", -EINVAL, NULL);
+ test_dns_service_join_one("foo", "foo", "foo", -EINVAL, NULL);
+
+ test_dns_service_join_one("foo", "_http._tcp", "", 0, "foo._http._tcp");
+ test_dns_service_join_one(NULL, "_http._tcp", "", 0, "_http._tcp");
+ test_dns_service_join_one("foo", "_http._tcp", "foo", 0, "foo._http._tcp.foo");
+ test_dns_service_join_one(NULL, "_http._tcp", "foo", 0, "_http._tcp.foo");
+ test_dns_service_join_one("Lennart's PC", "_pc._tcp", "foo.bar.com", 0, "Lennart\\039s\\032PC._pc._tcp.foo.bar.com");
+ test_dns_service_join_one(NULL, "_pc._tcp", "foo.bar.com", 0, "_pc._tcp.foo.bar.com");
+}
+
+static void test_dns_service_split_one(const char *joined, const char *a, const char *b, const char *c, int r) {
+ _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL, *t = NULL;
+
+ assert_se(dns_service_split(joined, &x, &y, &z) == r);
+ assert_se(streq_ptr(x, a));
+ assert_se(streq_ptr(y, b));
+ assert_se(streq_ptr(z, c));
+
+ if (r < 0)
+ return;
+
+ if (y) {
+ assert_se(dns_service_join(x, y, z, &t) == 0);
+ assert_se(streq_ptr(joined, t));
+ } else
+ assert_se(!x && streq_ptr(z, joined));
+}
+
+static void test_dns_service_split(void) {
+ test_dns_service_split_one("", NULL, NULL, "", 0);
+ test_dns_service_split_one("foo", NULL, NULL, "foo", 0);
+ test_dns_service_split_one("foo.bar", NULL, NULL, "foo.bar", 0);
+ test_dns_service_split_one("_foo.bar", NULL, NULL, "_foo.bar", 0);
+ test_dns_service_split_one("_foo._bar", NULL, "_foo._bar", "", 0);
+ test_dns_service_split_one("_meh._foo._bar", "_meh", "_foo._bar", "", 0);
+ test_dns_service_split_one("Wuff\\032Wuff._foo._bar.waldo.com", "Wuff Wuff", "_foo._bar", "waldo.com", 0);
+}
+
+static void test_dns_name_change_suffix_one(const char *name, const char *old_suffix, const char *new_suffix, int r, const char *result) {
+ _cleanup_free_ char *s = NULL;
+
+ assert_se(dns_name_change_suffix(name, old_suffix, new_suffix, &s) == r);
+ assert_se(streq_ptr(s, result));
+}
+
+static void test_dns_name_change_suffix(void) {
+ test_dns_name_change_suffix_one("foo.bar", "bar", "waldo", 1, "foo.waldo");
+ test_dns_name_change_suffix_one("foo.bar.waldi.quux", "foo.bar.waldi.quux", "piff.paff", 1, "piff.paff");
+ test_dns_name_change_suffix_one("foo.bar.waldi.quux", "bar.waldi.quux", "piff.paff", 1, "foo.piff.paff");
+ test_dns_name_change_suffix_one("foo.bar.waldi.quux", "waldi.quux", "piff.paff", 1, "foo.bar.piff.paff");
+ test_dns_name_change_suffix_one("foo.bar.waldi.quux", "quux", "piff.paff", 1, "foo.bar.waldi.piff.paff");
+ test_dns_name_change_suffix_one("foo.bar.waldi.quux", "", "piff.paff", 1, "foo.bar.waldi.quux.piff.paff");
+ test_dns_name_change_suffix_one("", "", "piff.paff", 1, "piff.paff");
+ test_dns_name_change_suffix_one("", "", "", 1, "");
+ test_dns_name_change_suffix_one("a", "b", "c", 0, NULL);
+}
+
+static void test_dns_name_suffix_one(const char *name, unsigned n_labels, const char *result, int ret) {
+ const char *p = NULL;
+
+ assert_se(ret == dns_name_suffix(name, n_labels, &p));
+ assert_se(streq_ptr(p, result));
+}
+
+static void test_dns_name_suffix(void) {
+ test_dns_name_suffix_one("foo.bar", 2, "foo.bar", 0);
+ test_dns_name_suffix_one("foo.bar", 1, "bar", 1);
+ test_dns_name_suffix_one("foo.bar", 0, "", 2);
+ test_dns_name_suffix_one("foo.bar", 3, NULL, -EINVAL);
+ test_dns_name_suffix_one("foo.bar", 4, NULL, -EINVAL);
+
+ test_dns_name_suffix_one("bar", 1, "bar", 0);
+ test_dns_name_suffix_one("bar", 0, "", 1);
+ test_dns_name_suffix_one("bar", 2, NULL, -EINVAL);
+ test_dns_name_suffix_one("bar", 3, NULL, -EINVAL);
+
+ test_dns_name_suffix_one("", 0, "", 0);
+ test_dns_name_suffix_one("", 1, NULL, -EINVAL);
+ test_dns_name_suffix_one("", 2, NULL, -EINVAL);
+}
+
+static void test_dns_name_count_labels_one(const char *name, int n) {
+ assert_se(dns_name_count_labels(name) == n);
+}
+
+static void test_dns_name_count_labels(void) {
+ test_dns_name_count_labels_one("foo.bar.quux.", 3);
+ test_dns_name_count_labels_one("foo.bar.quux", 3);
+ test_dns_name_count_labels_one("foo.bar.", 2);
+ test_dns_name_count_labels_one("foo.bar", 2);
+ test_dns_name_count_labels_one("foo.", 1);
+ test_dns_name_count_labels_one("foo", 1);
+ test_dns_name_count_labels_one("", 0);
+ test_dns_name_count_labels_one(".", 0);
+ test_dns_name_count_labels_one("..", -EINVAL);
+}
+
+static void test_dns_name_equal_skip_one(const char *a, unsigned n_labels, const char *b, int ret) {
+ assert_se(dns_name_equal_skip(a, n_labels, b) == ret);
+}
+
+static void test_dns_name_equal_skip(void) {
+ test_dns_name_equal_skip_one("foo", 0, "bar", 0);
+ test_dns_name_equal_skip_one("foo", 0, "foo", 1);
+ test_dns_name_equal_skip_one("foo", 1, "foo", 0);
+ test_dns_name_equal_skip_one("foo", 2, "foo", 0);
+
+ test_dns_name_equal_skip_one("foo.bar", 0, "foo.bar", 1);
+ test_dns_name_equal_skip_one("foo.bar", 1, "foo.bar", 0);
+ test_dns_name_equal_skip_one("foo.bar", 2, "foo.bar", 0);
+ test_dns_name_equal_skip_one("foo.bar", 3, "foo.bar", 0);
+
+ test_dns_name_equal_skip_one("foo.bar", 0, "bar", 0);
+ test_dns_name_equal_skip_one("foo.bar", 1, "bar", 1);
+ test_dns_name_equal_skip_one("foo.bar", 2, "bar", 0);
+ test_dns_name_equal_skip_one("foo.bar", 3, "bar", 0);
+
+ test_dns_name_equal_skip_one("foo.bar", 0, "", 0);
+ test_dns_name_equal_skip_one("foo.bar", 1, "", 0);
+ test_dns_name_equal_skip_one("foo.bar", 2, "", 1);
+ test_dns_name_equal_skip_one("foo.bar", 3, "", 0);
+
+ test_dns_name_equal_skip_one("", 0, "", 1);
+ test_dns_name_equal_skip_one("", 1, "", 0);
+ test_dns_name_equal_skip_one("", 1, "foo", 0);
+ test_dns_name_equal_skip_one("", 2, "foo", 0);
+}
+
+static void test_dns_name_compare_func(void) {
+ assert_se(dns_name_compare_func("", "") == 0);
+ assert_se(dns_name_compare_func("", ".") == 0);
+ assert_se(dns_name_compare_func(".", "") == 0);
+ assert_se(dns_name_compare_func("foo", "foo.") == 0);
+ assert_se(dns_name_compare_func("foo.", "foo") == 0);
+ assert_se(dns_name_compare_func("foo", "foo") == 0);
+ assert_se(dns_name_compare_func("foo.", "foo.") == 0);
+ assert_se(dns_name_compare_func("heise.de", "HEISE.DE.") == 0);
+
+ assert_se(dns_name_compare_func("de.", "heise.de") != 0);
+}
+
+static void test_dns_name_common_suffix_one(const char *a, const char *b, const char *result) {
+ const char *c;
+
+ assert_se(dns_name_common_suffix(a, b, &c) >= 0);
+ assert_se(streq(c, result));
+}
+
+static void test_dns_name_common_suffix(void) {
+ test_dns_name_common_suffix_one("", "", "");
+ test_dns_name_common_suffix_one("foo", "", "");
+ test_dns_name_common_suffix_one("", "foo", "");
+ test_dns_name_common_suffix_one("foo", "bar", "");
+ test_dns_name_common_suffix_one("bar", "foo", "");
+ test_dns_name_common_suffix_one("foo", "foo", "foo");
+ test_dns_name_common_suffix_one("quux.foo", "foo", "foo");
+ test_dns_name_common_suffix_one("foo", "quux.foo", "foo");
+ test_dns_name_common_suffix_one("this.is.a.short.sentence", "this.is.another.short.sentence", "short.sentence");
+ test_dns_name_common_suffix_one("FOO.BAR", "tEST.bAR", "BAR");
+}
+
+static void test_dns_name_apply_idna_one(const char *s, const char *result) {
+#ifdef HAVE_LIBIDN
+ _cleanup_free_ char *buf = NULL;
+ assert_se(dns_name_apply_idna(s, &buf) >= 0);
+ assert_se(dns_name_equal(buf, result) > 0);
+#endif
+}
+
+static void test_dns_name_apply_idna(void) {
+ test_dns_name_apply_idna_one("", "");
+ test_dns_name_apply_idna_one("foo", "foo");
+ test_dns_name_apply_idna_one("foo.", "foo");
+ test_dns_name_apply_idna_one("foo.bar", "foo.bar");
+ test_dns_name_apply_idna_one("foo.bar.", "foo.bar");
+ test_dns_name_apply_idna_one("föö", "xn--f-1gaa");
+ test_dns_name_apply_idna_one("föö.", "xn--f-1gaa");
+ test_dns_name_apply_idna_one("föö.bär", "xn--f-1gaa.xn--br-via");
+ test_dns_name_apply_idna_one("föö.bär.", "xn--f-1gaa.xn--br-via");
}
int main(int argc, char *argv[]) {
@@ -292,12 +626,25 @@ int main(int argc, char *argv[]) {
test_dns_name_normalize();
test_dns_name_equal();
test_dns_name_endswith();
+ test_dns_name_startswith();
test_dns_name_between();
- test_dns_name_root();
- test_dns_name_single_label();
+ test_dns_name_is_root();
+ test_dns_name_is_single_label();
test_dns_name_reverse();
test_dns_name_concat();
test_dns_name_is_valid();
+ test_dns_name_to_wire_format();
+ test_dns_service_name_is_valid();
+ test_dns_srv_type_is_valid();
+ test_dns_service_join();
+ test_dns_service_split();
+ test_dns_name_change_suffix();
+ test_dns_name_suffix();
+ test_dns_name_count_labels();
+ test_dns_name_equal_skip();
+ test_dns_name_compare_func();
+ test_dns_name_common_suffix();
+ test_dns_name_apply_idna();
return 0;
}
diff --git a/src/test/test-ellipsize.c b/src/test/test-ellipsize.c
index 27df9089c3..c597d5aecd 100644
--- a/src/test/test-ellipsize.c
+++ b/src/test/test-ellipsize.c
@@ -21,9 +21,11 @@
#include <stdio.h>
-#include "util.h"
-#include "terminal-util.h"
+#include "alloc-util.h"
#include "def.h"
+#include "string-util.h"
+#include "terminal-util.h"
+#include "util.h"
static void test_one(const char *p) {
_cleanup_free_ char *t;
diff --git a/src/test/test-engine.c b/src/test/test-engine.c
index 6596069ade..e23eec7370 100644
--- a/src/test/test-engine.c
+++ b/src/test/test-engine.c
@@ -19,15 +19,16 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
#include <errno.h>
+#include <stdio.h>
#include <string.h>
-#include "manager.h"
#include "bus-util.h"
+#include "manager.h"
+#include "test-helper.h"
int main(int argc, char *argv[]) {
- _cleanup_bus_error_free_ sd_bus_error err = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL;
Manager *m = NULL;
Unit *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL, *g = NULL, *h = NULL;
FILE *serial = NULL;
@@ -38,8 +39,8 @@ int main(int argc, char *argv[]) {
/* prepare the test */
assert_se(set_unit_path(TEST_DIR) >= 0);
r = manager_new(MANAGER_USER, true, &m);
- if (IN_SET(r, -EPERM, -EACCES, -EADDRINUSE, -EHOSTDOWN, -ENOENT, -ENOEXEC)) {
- printf("Skipping test: manager_new: %s", strerror(-r));
+ if (MANAGER_SKIP_TEST(r)) {
+ printf("Skipping test: manager_new: %s\n", strerror(-r));
return EXIT_TEST_SKIP;
}
assert_se(r >= 0);
@@ -52,7 +53,7 @@ int main(int argc, char *argv[]) {
manager_dump_units(m, stdout, "\t");
printf("Test1: (Trivial)\n");
- r = manager_add_job(m, JOB_START, c, JOB_REPLACE, false, &err, &j);
+ r = manager_add_job(m, JOB_START, c, JOB_REPLACE, &err, &j);
if (sd_bus_error_is_set(&err))
log_error("error: %s: %s", err.name, err.message);
assert_se(r == 0);
@@ -65,15 +66,15 @@ 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) == -EDEADLK);
+ assert_se(manager_add_job(m, JOB_START, d, JOB_REPLACE, NULL, &j) == -EDEADLK);
manager_dump_jobs(m, stdout, "\t");
printf("Test3: (Cyclic Order, Fixable, Garbage Collector)\n");
- assert_se(manager_add_job(m, JOB_START, e, JOB_REPLACE, false, NULL, &j) == 0);
+ assert_se(manager_add_job(m, JOB_START, e, JOB_REPLACE, NULL, &j) == 0);
manager_dump_jobs(m, stdout, "\t");
printf("Test4: (Identical transaction)\n");
- assert_se(manager_add_job(m, JOB_START, e, JOB_FAIL, false, NULL, &j) == 0);
+ assert_se(manager_add_job(m, JOB_START, e, JOB_FAIL, NULL, &j) == 0);
manager_dump_jobs(m, stdout, "\t");
printf("Load3:\n");
@@ -81,21 +82,21 @@ 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) == -EDEADLK);
+ assert_se(manager_add_job(m, JOB_START, g, JOB_FAIL, NULL, &j) == -EDEADLK);
printf("Test6: (Colliding transaction, replace)\n");
- assert_se(manager_add_job(m, JOB_START, g, JOB_REPLACE, false, NULL, &j) == 0);
+ assert_se(manager_add_job(m, JOB_START, g, JOB_REPLACE, 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) == -EDEADLK);
+ assert_se(manager_add_job(m, JOB_STOP, g, JOB_FAIL, 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);
+ assert_se(manager_add_job(m, JOB_RESTART, g, JOB_FAIL, NULL, &j) == 0);
manager_dump_jobs(m, stdout, "\t");
printf("Test9: (Unmergeable job type, replace)\n");
- assert_se(manager_add_job(m, JOB_STOP, g, JOB_REPLACE, false, NULL, &j) == 0);
+ assert_se(manager_add_job(m, JOB_STOP, g, JOB_REPLACE, NULL, &j) == 0);
manager_dump_jobs(m, stdout, "\t");
printf("Load4:\n");
@@ -103,7 +104,7 @@ int main(int argc, char *argv[]) {
manager_dump_units(m, stdout, "\t");
printf("Test10: (Unmergeable job type of auxiliary job, fail)\n");
- assert_se(manager_add_job(m, JOB_START, h, JOB_FAIL, false, NULL, &j) == 0);
+ assert_se(manager_add_job(m, JOB_START, h, JOB_FAIL, NULL, &j) == 0);
manager_dump_jobs(m, stdout, "\t");
manager_free(m);
diff --git a/src/test/test-env-replace.c b/src/test/test-env-replace.c
index 2e28c0c49b..c1315bbf9f 100644
--- a/src/test/test-env-replace.c
+++ b/src/test/test-env-replace.c
@@ -21,9 +21,10 @@
#include <string.h>
-#include "util.h"
-#include "strv.h"
#include "env-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "util.h"
static void test_strv_env_delete(void) {
_cleanup_strv_free_ char **a = NULL, **b = NULL, **c = NULL, **d = NULL;
@@ -118,6 +119,8 @@ static void test_replace_env_arg(void) {
"$FOO$FOO",
"${FOO}${BAR}",
"${FOO",
+ "FOO$$${FOO}",
+ "$$FOO${FOO}",
NULL
};
_cleanup_strv_free_ char **r = NULL;
@@ -133,7 +136,9 @@ static void test_replace_env_arg(void) {
assert_se(streq(r[6], "BAR"));
assert_se(streq(r[7], "BAR BARwaldo"));
assert_se(streq(r[8], "${FOO"));
- assert_se(strv_length(r) == 9);
+ assert_se(streq(r[9], "FOO$BAR BAR"));
+ assert_se(streq(r[10], "$FOOBAR BAR"));
+ assert_se(strv_length(r) == 11);
}
static void test_env_clean(void) {
diff --git a/src/test/test-execute.c b/src/test/test-execute.c
index dd8ab7dcb8..92857cb5e2 100644
--- a/src/test/test-execute.c
+++ b/src/test/test-execute.c
@@ -17,14 +17,22 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <grp.h>
+#include <pwd.h>
#include <stdio.h>
+#include <sys/prctl.h>
+#include <sys/types.h>
-#include "unit.h"
-#include "manager.h"
-#include "util.h"
+#include "fileio.h"
+#include "fs-util.h"
#include "macro.h"
+#include "manager.h"
#include "mkdir.h"
+#include "path-util.h"
#include "rm-rf.h"
+#include "test-helper.h"
+#include "unit.h"
+#include "util.h"
typedef void (*test_function_t)(Manager *m);
@@ -77,10 +85,14 @@ static void test_exec_workingdirectory(Manager *m) {
}
static void test_exec_personality(Manager *m) {
- test(m, "exec-personality-x86.service", 0, CLD_EXITED);
-
#if defined(__x86_64__)
test(m, "exec-personality-x86-64.service", 0, CLD_EXITED);
+
+#elif defined(__s390__)
+ test(m, "exec-personality-s390.service", 0, CLD_EXITED);
+
+#else
+ test(m, "exec-personality-x86.service", 0, CLD_EXITED);
#endif
}
@@ -119,11 +131,17 @@ static void test_exec_systemcallerrornumber(Manager *m) {
}
static void test_exec_user(Manager *m) {
- test(m, "exec-user.service", 0, CLD_EXITED);
+ if (getpwnam("nobody"))
+ test(m, "exec-user.service", 0, CLD_EXITED);
+ else
+ log_error_errno(errno, "Skipping test_exec_user, could not find nobody user: %m");
}
static void test_exec_group(Manager *m) {
- test(m, "exec-group.service", 0, CLD_EXITED);
+ if (getgrnam("nobody"))
+ test(m, "exec-group.service", 0, CLD_EXITED);
+ else
+ log_error_errno(errno, "Skipping test_exec_group, could not find nobody group: %m");
}
static void test_exec_environment(Manager *m) {
@@ -132,6 +150,50 @@ static void test_exec_environment(Manager *m) {
test(m, "exec-environment-empty.service", 0, CLD_EXITED);
}
+static void test_exec_environmentfile(Manager *m) {
+ static const char e[] =
+ "VAR1='word1 word2'\n"
+ "VAR2=word3 \n"
+ "# comment1\n"
+ "\n"
+ "; comment2\n"
+ " ; # comment3\n"
+ "line without an equal\n"
+ "VAR3='$word 5 6'\n";
+ int r;
+
+ r = write_string_file("/tmp/test-exec_environmentfile.conf", e, WRITE_STRING_FILE_CREATE);
+ assert_se(r == 0);
+
+ test(m, "exec-environmentfile.service", 0, CLD_EXITED);
+
+ unlink("/tmp/test-exec_environmentfile.conf");
+}
+
+static void test_exec_passenvironment(Manager *m) {
+ /* test-execute runs under MANAGER_USER which, by default, forwards all
+ * variables present in the environment, but only those that are
+ * present _at the time it is created_!
+ *
+ * So these PassEnvironment checks are still expected to work, since we
+ * are ensuring the variables are not present at manager creation (they
+ * are unset explicitly in main) and are only set here.
+ *
+ * This is still a good approximation of how a test for MANAGER_SYSTEM
+ * would work.
+ */
+ assert_se(setenv("VAR1", "word1 word2", 1) == 0);
+ assert_se(setenv("VAR2", "word3", 1) == 0);
+ assert_se(setenv("VAR3", "$word 5 6", 1) == 0);
+ test(m, "exec-passenvironment.service", 0, CLD_EXITED);
+ test(m, "exec-passenvironment-repeated.service", 0, CLD_EXITED);
+ test(m, "exec-passenvironment-empty.service", 0, CLD_EXITED);
+ assert_se(unsetenv("VAR1") == 0);
+ assert_se(unsetenv("VAR2") == 0);
+ assert_se(unsetenv("VAR3") == 0);
+ test(m, "exec-passenvironment-absent.service", 0, CLD_EXITED);
+}
+
static void test_exec_umask(Manager *m) {
test(m, "exec-umask-default.service", 0, CLD_EXITED);
test(m, "exec-umask-0177.service", 0, CLD_EXITED);
@@ -140,7 +202,65 @@ static void test_exec_umask(Manager *m) {
static void test_exec_runtimedirectory(Manager *m) {
test(m, "exec-runtimedirectory.service", 0, CLD_EXITED);
test(m, "exec-runtimedirectory-mode.service", 0, CLD_EXITED);
- test(m, "exec-runtimedirectory-owner.service", 0, CLD_EXITED);
+ if (getgrnam("nobody"))
+ test(m, "exec-runtimedirectory-owner.service", 0, CLD_EXITED);
+ else
+ log_error_errno(errno, "Skipping test_exec_runtimedirectory-owner, could not find nobody group: %m");
+}
+
+static void test_exec_capabilityboundingset(Manager *m) {
+ int r;
+
+ /* We use capsh to test if the capabilities are
+ * properly set, so be sure that it exists */
+ r = find_binary("capsh", NULL);
+ if (r < 0) {
+ log_error_errno(r, "Skipping test_exec_capabilityboundingset, could not find capsh binary: %m");
+ return;
+ }
+
+ test(m, "exec-capabilityboundingset-simple.service", 0, CLD_EXITED);
+ test(m, "exec-capabilityboundingset-reset.service", 0, CLD_EXITED);
+ test(m, "exec-capabilityboundingset-merge.service", 0, CLD_EXITED);
+ test(m, "exec-capabilityboundingset-invert.service", 0, CLD_EXITED);
+}
+
+static void test_exec_capabilityambientset(Manager *m) {
+ int r;
+
+ /* Check if the kernel has support for ambient capabilities. Run
+ * the tests only if that's the case. Clearing all ambient
+ * capabilities is fine, since we are expecting them to be unset
+ * in the first place for the tests. */
+ r = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0);
+ if (r >= 0 || errno != EINVAL) {
+ test(m, "exec-capabilityambientset.service", 0, CLD_EXITED);
+ test(m, "exec-capabilityambientset-merge.service", 0, CLD_EXITED);
+ }
+}
+
+static void test_exec_privatenetwork(Manager *m) {
+ int r;
+
+ r = find_binary("ip", NULL);
+ if (r < 0) {
+ log_error_errno(r, "Skipping test_exec_privatenetwork, could not find ip binary: %m");
+ return;
+ }
+
+ test(m, "exec-privatenetwork-yes.service", 0, CLD_EXITED);
+}
+
+static void test_exec_oomscoreadjust(Manager *m) {
+ test(m, "exec-oomscoreadjust-positive.service", 0, CLD_EXITED);
+ test(m, "exec-oomscoreadjust-negative.service", 0, CLD_EXITED);
+}
+
+static void test_exec_ioschedulingclass(Manager *m) {
+ test(m, "exec-ioschedulingclass-none.service", 0, CLD_EXITED);
+ test(m, "exec-ioschedulingclass-idle.service", 0, CLD_EXITED);
+ test(m, "exec-ioschedulingclass-realtime.service", 0, CLD_EXITED);
+ test(m, "exec-ioschedulingclass-best-effort.service", 0, CLD_EXITED);
}
int main(int argc, char *argv[]) {
@@ -150,13 +270,20 @@ int main(int argc, char *argv[]) {
test_exec_ignoresigpipe,
test_exec_privatetmp,
test_exec_privatedevices,
+ test_exec_privatenetwork,
test_exec_systemcallfilter,
test_exec_systemcallerrornumber,
test_exec_user,
test_exec_group,
test_exec_environment,
+ test_exec_environmentfile,
+ test_exec_passenvironment,
test_exec_umask,
test_exec_runtimedirectory,
+ test_exec_capabilityboundingset,
+ test_exec_capabilityambientset,
+ test_exec_oomscoreadjust,
+ test_exec_ioschedulingclass,
NULL,
};
test_function_t *test = NULL;
@@ -173,11 +300,21 @@ int main(int argc, char *argv[]) {
}
assert_se(setenv("XDG_RUNTIME_DIR", "/tmp/", 1) == 0);
- assert_se(set_unit_path(TEST_DIR) >= 0);
+ assert_se(set_unit_path(TEST_DIR "/test-execute/") >= 0);
+
+ /* Unset VAR1, VAR2 and VAR3 which are used in the PassEnvironment test
+ * cases, otherwise (and if they are present in the environment),
+ * `manager_default_environment` will copy them into the default
+ * environment which is passed to each created job, which will make the
+ * tests that expect those not to be present to fail.
+ */
+ assert_se(unsetenv("VAR1") == 0);
+ assert_se(unsetenv("VAR2") == 0);
+ assert_se(unsetenv("VAR3") == 0);
r = manager_new(MANAGER_USER, true, &m);
- if (IN_SET(r, -EPERM, -EACCES, -EADDRINUSE, -EHOSTDOWN, -ENOENT)) {
- printf("Skipping test: manager_new: %s", strerror(-r));
+ if (MANAGER_SKIP_TEST(r)) {
+ printf("Skipping test: manager_new: %s\n", strerror(-r));
return EXIT_TEST_SKIP;
}
assert_se(r >= 0);
diff --git a/src/test/test-extract-word.c b/src/test/test-extract-word.c
new file mode 100644
index 0000000000..65d3a0a96e
--- /dev/null
+++ b/src/test/test-extract-word.c
@@ -0,0 +1,558 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+ Copyright 2013 Thomas H.P. Andersen
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "extract-word.h"
+#include "log.h"
+#include "string-util.h"
+
+static void test_extract_first_word(void) {
+ const char *p, *original;
+ char *t;
+
+ p = original = "foobar waldo";
+ assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
+ assert_se(streq(t, "foobar"));
+ free(t);
+ assert_se(p == original + 7);
+
+ assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
+ assert_se(streq(t, "waldo"));
+ free(t);
+ assert_se(isempty(p));
+
+ assert_se(extract_first_word(&p, &t, NULL, 0) == 0);
+ assert_se(!t);
+ assert_se(isempty(p));
+
+ p = original = "\"foobar\" \'waldo\'";
+ assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
+ assert_se(streq(t, "\"foobar\""));
+ free(t);
+ assert_se(p == original + 9);
+
+ assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
+ assert_se(streq(t, "\'waldo\'"));
+ free(t);
+ assert_se(isempty(p));
+
+ assert_se(extract_first_word(&p, &t, NULL, 0) == 0);
+ assert_se(!t);
+ assert_se(isempty(p));
+
+ p = original = "\"foobar\" \'waldo\'";
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) > 0);
+ assert_se(streq(t, "foobar"));
+ free(t);
+ assert_se(p == original + 9);
+
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) > 0);
+ assert_se(streq(t, "waldo"));
+ free(t);
+ assert_se(isempty(p));
+
+ assert_se(extract_first_word(&p, &t, NULL, 0) == 0);
+ assert_se(!t);
+ assert_se(isempty(p));
+
+ p = original = "\"";
+ assert_se(extract_first_word(&p, &t, NULL, 0) == 1);
+ assert_se(streq(t, "\""));
+ free(t);
+ assert_se(isempty(p));
+
+ p = original = "\"";
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) == -EINVAL);
+ assert_se(p == original + 1);
+
+ p = original = "\'";
+ assert_se(extract_first_word(&p, &t, NULL, 0) == 1);
+ assert_se(streq(t, "\'"));
+ free(t);
+ assert_se(isempty(p));
+
+ p = original = "\'";
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) == -EINVAL);
+ assert_se(p == original + 1);
+
+ p = original = "\'fooo";
+ assert_se(extract_first_word(&p, &t, NULL, 0) == 1);
+ assert_se(streq(t, "\'fooo"));
+ free(t);
+ assert_se(isempty(p));
+
+ p = original = "\'fooo";
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) == -EINVAL);
+ assert_se(p == original + 5);
+
+ p = original = "\'fooo";
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_RELAX) > 0);
+ assert_se(streq(t, "fooo"));
+ free(t);
+ assert_se(isempty(p));
+
+ p = original = "\"fooo";
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_RELAX) > 0);
+ assert_se(streq(t, "fooo"));
+ free(t);
+ assert_se(isempty(p));
+
+ p = original = "yay\'foo\'bar";
+ assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
+ assert_se(streq(t, "yay\'foo\'bar"));
+ free(t);
+ assert_se(isempty(p));
+
+ p = original = "yay\'foo\'bar";
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) > 0);
+ assert_se(streq(t, "yayfoobar"));
+ free(t);
+ assert_se(isempty(p));
+
+ p = original = " foobar ";
+ assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
+ assert_se(streq(t, "foobar"));
+ free(t);
+ assert_se(isempty(p));
+
+ p = original = " foo\\ba\\x6ar ";
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE) > 0);
+ assert_se(streq(t, "foo\ba\x6ar"));
+ free(t);
+ assert_se(isempty(p));
+
+ p = original = " foo\\ba\\x6ar ";
+ assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
+ assert_se(streq(t, "foobax6ar"));
+ free(t);
+ assert_se(isempty(p));
+
+ p = original = " f\\u00f6o \"pi\\U0001F4A9le\" ";
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE) > 0);
+ assert_se(streq(t, "föo"));
+ free(t);
+ assert_se(p == original + 13);
+
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE) > 0);
+ assert_se(streq(t, "pi\360\237\222\251le"));
+ free(t);
+ assert_se(isempty(p));
+
+ p = original = "fooo\\";
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_RELAX) > 0);
+ assert_se(streq(t, "fooo"));
+ free(t);
+ assert_se(isempty(p));
+
+ p = original = "fooo\\";
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE_RELAX) > 0);
+ assert_se(streq(t, "fooo\\"));
+ free(t);
+ assert_se(isempty(p));
+
+ p = original = "fooo\\";
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE_RELAX|EXTRACT_RELAX) > 0);
+ assert_se(streq(t, "fooo\\"));
+ free(t);
+ assert_se(isempty(p));
+
+ p = original = "fooo\\";
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX) > 0);
+ assert_se(streq(t, "fooo\\"));
+ free(t);
+ assert_se(isempty(p));
+
+ p = original = "\"foo\\";
+ assert_se(extract_first_word(&p, &t, NULL, 0) == -EINVAL);
+ assert_se(p == original + 5);
+
+ p = original = "\"foo\\";
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_RELAX) > 0);
+ assert_se(streq(t, "foo"));
+ free(t);
+ assert_se(isempty(p));
+
+ p = original = "foo::bar";
+ assert_se(extract_first_word(&p, &t, ":", 0) == 1);
+ assert_se(streq(t, "foo"));
+ free(t);
+ assert_se(p == original + 5);
+
+ assert_se(extract_first_word(&p, &t, ":", 0) == 1);
+ assert_se(streq(t, "bar"));
+ free(t);
+ assert_se(isempty(p));
+
+ assert_se(extract_first_word(&p, &t, ":", 0) == 0);
+ assert_se(!t);
+ assert_se(isempty(p));
+
+ p = original = "foo\\:bar::waldo";
+ assert_se(extract_first_word(&p, &t, ":", 0) == 1);
+ assert_se(streq(t, "foo:bar"));
+ free(t);
+ assert_se(p == original + 10);
+
+ assert_se(extract_first_word(&p, &t, ":", 0) == 1);
+ assert_se(streq(t, "waldo"));
+ free(t);
+ assert_se(isempty(p));
+
+ assert_se(extract_first_word(&p, &t, ":", 0) == 0);
+ assert_se(!t);
+ assert_se(isempty(p));
+
+ p = original = "\"foo\\";
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE_RELAX) == -EINVAL);
+ assert_se(p == original + 5);
+
+ p = original = "\"foo\\";
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE_RELAX|EXTRACT_RELAX) > 0);
+ assert_se(streq(t, "foo\\"));
+ free(t);
+ assert_se(isempty(p));
+
+ p = original = "\"foo\\";
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX|EXTRACT_RELAX) > 0);
+ assert_se(streq(t, "foo\\"));
+ free(t);
+ assert_se(isempty(p));
+
+ p = original = "fooo\\ bar quux";
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_RELAX) > 0);
+ assert_se(streq(t, "fooo bar"));
+ free(t);
+ assert_se(p == original + 10);
+
+ p = original = "fooo\\ bar quux";
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE_RELAX) > 0);
+ assert_se(streq(t, "fooo bar"));
+ free(t);
+ assert_se(p == original + 10);
+
+ p = original = "fooo\\ bar quux";
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE_RELAX|EXTRACT_RELAX) > 0);
+ assert_se(streq(t, "fooo bar"));
+ free(t);
+ assert_se(p == original + 10);
+
+ p = original = "fooo\\ bar quux";
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE) == -EINVAL);
+ assert_se(p == original + 5);
+
+ p = original = "fooo\\ bar quux";
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX) > 0);
+ assert_se(streq(t, "fooo\\ bar"));
+ free(t);
+ assert_se(p == original + 10);
+
+ p = original = "\\w+@\\K[\\d.]+";
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE) == -EINVAL);
+ assert_se(p == original + 1);
+
+ p = original = "\\w+@\\K[\\d.]+";
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX) > 0);
+ assert_se(streq(t, "\\w+@\\K[\\d.]+"));
+ free(t);
+ assert_se(isempty(p));
+
+ p = original = "\\w+\\b";
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX) > 0);
+ assert_se(streq(t, "\\w+\b"));
+ free(t);
+ assert_se(isempty(p));
+
+ p = original = "-N ''";
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) > 0);
+ assert_se(streq(t, "-N"));
+ free(t);
+ assert_se(p == original + 3);
+
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) > 0);
+ assert_se(streq(t, ""));
+ free(t);
+ assert_se(isempty(p));
+
+ p = original = ":foo\\:bar::waldo:";
+ assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 1);
+ assert_se(t);
+ assert_se(streq(t, ""));
+ free(t);
+ assert_se(p == original + 1);
+
+ assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 1);
+ assert_se(streq(t, "foo:bar"));
+ free(t);
+ assert_se(p == original + 10);
+
+ assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 1);
+ assert_se(t);
+ assert_se(streq(t, ""));
+ free(t);
+ assert_se(p == original + 11);
+
+ assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 1);
+ assert_se(streq(t, "waldo"));
+ free(t);
+ assert_se(p == original + 17);
+
+ assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 1);
+ assert_se(streq(t, ""));
+ free(t);
+ assert_se(p == NULL);
+
+ assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 0);
+ assert_se(!t);
+ assert_se(!p);
+
+ p = "foo\\xbar";
+ assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
+ assert_se(streq(t, "fooxbar"));
+ free(t);
+ assert_se(p == NULL);
+
+ p = "foo\\xbar";
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_RETAIN_ESCAPE) > 0);
+ assert_se(streq(t, "foo\\xbar"));
+ free(t);
+ assert_se(p == NULL);
+}
+
+static void test_extract_first_word_and_warn(void) {
+ const char *p, *original;
+ char *t;
+
+ p = original = "foobar waldo";
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) > 0);
+ assert_se(streq(t, "foobar"));
+ free(t);
+ assert_se(p == original + 7);
+
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) > 0);
+ assert_se(streq(t, "waldo"));
+ free(t);
+ assert_se(isempty(p));
+
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) == 0);
+ assert_se(!t);
+ assert_se(isempty(p));
+
+ p = original = "\"foobar\" \'waldo\'";
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES, NULL, "fake", 1, original) > 0);
+ assert_se(streq(t, "foobar"));
+ free(t);
+ assert_se(p == original + 9);
+
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES, NULL, "fake", 1, original) > 0);
+ assert_se(streq(t, "waldo"));
+ free(t);
+ assert_se(isempty(p));
+
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) == 0);
+ assert_se(!t);
+ assert_se(isempty(p));
+
+ p = original = "\"";
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES, NULL, "fake", 1, original) == -EINVAL);
+ assert_se(p == original + 1);
+
+ p = original = "\'";
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES, NULL, "fake", 1, original) == -EINVAL);
+ assert_se(p == original + 1);
+
+ p = original = "\'fooo";
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES, NULL, "fake", 1, original) == -EINVAL);
+ assert_se(p == original + 5);
+
+ p = original = "\'fooo";
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
+ assert_se(streq(t, "fooo"));
+ free(t);
+ assert_se(isempty(p));
+
+ p = original = " foo\\ba\\x6ar ";
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
+ assert_se(streq(t, "foo\ba\x6ar"));
+ free(t);
+ assert_se(isempty(p));
+
+ p = original = " foo\\ba\\x6ar ";
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) > 0);
+ assert_se(streq(t, "foobax6ar"));
+ free(t);
+ assert_se(isempty(p));
+
+ p = original = " f\\u00f6o \"pi\\U0001F4A9le\" ";
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
+ assert_se(streq(t, "föo"));
+ free(t);
+ assert_se(p == original + 13);
+
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
+ assert_se(streq(t, "pi\360\237\222\251le"));
+ free(t);
+ assert_se(isempty(p));
+
+ p = original = "fooo\\";
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
+ assert_se(streq(t, "fooo"));
+ free(t);
+ assert_se(isempty(p));
+
+ p = original = "fooo\\";
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) > 0);
+ assert_se(streq(t, "fooo\\"));
+ free(t);
+ assert_se(isempty(p));
+
+ p = original = "fooo\\";
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
+ assert_se(streq(t, "fooo\\"));
+ free(t);
+ assert_se(isempty(p));
+
+ p = original = "\"foo\\";
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES, NULL, "fake", 1, original) == -EINVAL);
+ assert_se(p == original + 5);
+
+ p = original = "\"foo\\";
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
+ assert_se(streq(t, "foo"));
+ free(t);
+ assert_se(isempty(p));
+
+ p = original = "\"foo\\";
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE, NULL, "fake", 1, original) == -EINVAL);
+ assert_se(p == original + 5);
+
+ p = original = "\"foo\\";
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE|EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
+ assert_se(streq(t, "foo"));
+ free(t);
+ assert_se(isempty(p));
+
+ p = original = "fooo\\ bar quux";
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
+ assert_se(streq(t, "fooo bar"));
+ free(t);
+ assert_se(p == original + 10);
+
+ p = original = "fooo\\ bar quux";
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) > 0);
+ assert_se(streq(t, "fooo bar"));
+ free(t);
+ assert_se(p == original + 10);
+
+ p = original = "fooo\\ bar quux";
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
+ assert_se(streq(t, "fooo\\ bar"));
+ free(t);
+ assert_se(p == original + 10);
+
+ p = original = "\\w+@\\K[\\d.]+";
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
+ assert_se(streq(t, "\\w+@\\K[\\d.]+"));
+ free(t);
+ assert_se(isempty(p));
+
+ p = original = "\\w+\\b";
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
+ assert_se(streq(t, "\\w+\b"));
+ free(t);
+ assert_se(isempty(p));
+}
+
+static void test_extract_many_words(void) {
+ const char *p, *original;
+ char *a, *b, *c;
+
+ p = original = "foobar waldi piep";
+ assert_se(extract_many_words(&p, NULL, 0, &a, &b, &c, NULL) == 3);
+ assert_se(isempty(p));
+ assert_se(streq_ptr(a, "foobar"));
+ assert_se(streq_ptr(b, "waldi"));
+ assert_se(streq_ptr(c, "piep"));
+ free(a);
+ free(b);
+ free(c);
+
+ p = original = "'foobar' wa\"ld\"i ";
+ assert_se(extract_many_words(&p, NULL, 0, &a, &b, &c, NULL) == 2);
+ assert_se(isempty(p));
+ assert_se(streq_ptr(a, "'foobar'"));
+ assert_se(streq_ptr(b, "wa\"ld\"i"));
+ assert_se(streq_ptr(c, NULL));
+ free(a);
+ free(b);
+
+ p = original = "'foobar' wa\"ld\"i ";
+ assert_se(extract_many_words(&p, NULL, EXTRACT_QUOTES, &a, &b, &c, NULL) == 2);
+ assert_se(isempty(p));
+ assert_se(streq_ptr(a, "foobar"));
+ assert_se(streq_ptr(b, "waldi"));
+ assert_se(streq_ptr(c, NULL));
+ free(a);
+ free(b);
+
+ p = original = "";
+ assert_se(extract_many_words(&p, NULL, 0, &a, &b, &c, NULL) == 0);
+ assert_se(isempty(p));
+ assert_se(streq_ptr(a, NULL));
+ assert_se(streq_ptr(b, NULL));
+ assert_se(streq_ptr(c, NULL));
+
+ p = original = " ";
+ assert_se(extract_many_words(&p, NULL, 0, &a, &b, &c, NULL) == 0);
+ assert_se(isempty(p));
+ assert_se(streq_ptr(a, NULL));
+ assert_se(streq_ptr(b, NULL));
+ assert_se(streq_ptr(c, NULL));
+
+ p = original = "foobar";
+ assert_se(extract_many_words(&p, NULL, 0, NULL) == 0);
+ assert_se(p == original);
+
+ p = original = "foobar waldi";
+ assert_se(extract_many_words(&p, NULL, 0, &a, NULL) == 1);
+ assert_se(p == original+7);
+ assert_se(streq_ptr(a, "foobar"));
+ free(a);
+
+ p = original = " foobar ";
+ assert_se(extract_many_words(&p, NULL, 0, &a, NULL) == 1);
+ assert_se(isempty(p));
+ assert_se(streq_ptr(a, "foobar"));
+ free(a);
+}
+
+int main(int argc, char *argv[]) {
+ log_parse_environment();
+ log_open();
+
+ test_extract_first_word();
+ test_extract_first_word_and_warn();
+ test_extract_many_words();
+
+ return 0;
+}
diff --git a/src/test/test-fdset.c b/src/test/test-fdset.c
index 242c5d9dc2..282aab1246 100644
--- a/src/test/test-fdset.c
+++ b/src/test/test-fdset.c
@@ -20,9 +20,11 @@
#include <fcntl.h>
#include <unistd.h>
+#include "fd-util.h"
#include "fdset.h"
-#include "util.h"
+#include "fileio.h"
#include "macro.h"
+#include "util.h"
static void test_fdset_new_fill(void) {
int fd = -1;
diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c
index be3a87958f..871c71e171 100644
--- a/src/test/test-fileio.c
+++ b/src/test/test-fileio.c
@@ -19,17 +19,21 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
#include <fcntl.h>
+#include <stdio.h>
#include <unistd.h>
-#include "util.h"
-#include "process-util.h"
+#include "alloc-util.h"
+#include "ctype.h"
+#include "def.h"
+#include "env-util.h"
+#include "fd-util.h"
#include "fileio.h"
+#include "parse-util.h"
+#include "process-util.h"
+#include "string-util.h"
#include "strv.h"
-#include "env-util.h"
-#include "def.h"
-#include "ctype.h"
+#include "util.h"
static void test_parse_env_file(void) {
char t[] = "/tmp/test-fileio-in-XXXXXX",
@@ -241,18 +245,18 @@ static void test_status_field(void) {
unsigned long long total = 0, buffers = 0;
int r;
- assert_se(get_status_field("/proc/self/status", "\nThreads:", &t) == 0);
+ assert_se(get_proc_field("/proc/self/status", "Threads", WHITESPACE, &t) == 0);
puts(t);
assert_se(streq(t, "1"));
- r = get_status_field("/proc/meminfo", "MemTotal:", &p);
+ r = get_proc_field("/proc/meminfo", "MemTotal", WHITESPACE, &p);
if (r != -ENOENT) {
assert_se(r == 0);
puts(p);
assert_se(safe_atollu(p, &total) == 0);
}
- r = get_status_field("/proc/meminfo", "\nBuffers:", &s);
+ r = get_proc_field("/proc/meminfo", "Buffers", WHITESPACE, &s);
if (r != -ENOENT) {
assert_se(r == 0);
puts(s);
@@ -263,7 +267,7 @@ static void test_status_field(void) {
assert_se(buffers < total);
/* Seccomp should be a good test for field full of zeros. */
- r = get_status_field("/proc/meminfo", "\nSeccomp:", &z);
+ r = get_proc_field("/proc/meminfo", "Seccomp", WHITESPACE, &z);
if (r != -ENOENT) {
assert_se(r == 0);
puts(z);
@@ -359,6 +363,26 @@ static void test_write_string_file_no_create(void) {
unlink(fn);
}
+static void test_write_string_file_verify(void) {
+ _cleanup_free_ char *buf = NULL, *buf2 = NULL;
+ int r;
+
+ assert_se(read_one_line_file("/proc/cmdline", &buf) >= 0);
+ assert_se((buf2 = strjoin(buf, "\n", NULL)));
+
+ r = write_string_file("/proc/cmdline", buf, 0);
+ assert_se(r == -EACCES || r == -EIO);
+ r = write_string_file("/proc/cmdline", buf2, 0);
+ assert_se(r == -EACCES || r == -EIO);
+
+ assert_se(write_string_file("/proc/cmdline", buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE) == 0);
+ assert_se(write_string_file("/proc/cmdline", buf2, WRITE_STRING_FILE_VERIFY_ON_FAILURE) == 0);
+
+ r = write_string_file("/proc/cmdline", buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_AVOID_NEWLINE);
+ assert_se(r == -EACCES || r == -EIO);
+ assert_se(write_string_file("/proc/cmdline", buf2, WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_AVOID_NEWLINE) == 0);
+}
+
static void test_load_env_file_pairs(void) {
char fn[] = "/tmp/test-load_env_file_pairs-XXXXXX";
int fd;
@@ -415,6 +439,7 @@ int main(int argc, char *argv[]) {
test_write_string_stream();
test_write_string_file();
test_write_string_file_no_create();
+ test_write_string_file_verify();
test_load_env_file_pairs();
return 0;
diff --git a/src/test/test-firewall-util.c b/src/test/test-firewall-util.c
index d636e427c4..ff66bde094 100644
--- a/src/test/test-firewall-util.c
+++ b/src/test/test-firewall-util.c
@@ -19,8 +19,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "log.h"
#include "firewall-util.h"
+#include "log.h"
#define MAKE_IN_ADDR_UNION(a,b,c,d) (union in_addr_union) { .in.s_addr = htobe32((uint32_t) (a) << 24 | (uint32_t) (b) << 16 | (uint32_t) (c) << 8 | (uint32_t) (d))}
diff --git a/src/test/test-fstab-util.c b/src/test/test-fstab-util.c
index 50e5dee0a7..27816ac779 100644
--- a/src/test/test-fstab-util.c
+++ b/src/test/test-fstab-util.c
@@ -19,9 +19,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "alloc-util.h"
#include "fstab-util.h"
-#include "util.h"
#include "log.h"
+#include "string-util.h"
+#include "util.h"
/*
int fstab_filter_options(const char *opts, const char *names,
diff --git a/src/test/test-hashmap-plain.c b/src/test/test-hashmap-plain.c
index 057b6c1dc1..6bf33306a9 100644
--- a/src/test/test-hashmap-plain.c
+++ b/src/test/test-hashmap-plain.c
@@ -17,9 +17,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "alloc-util.h"
+#include "hashmap.h"
+#include "string-util.h"
#include "strv.h"
#include "util.h"
-#include "hashmap.h"
void test_hashmap_funcs(void);
@@ -692,8 +694,8 @@ static void test_hashmap_get2(void) {
hashmap_free_free_free(m);
}
-static unsigned long crippled_hashmap_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
- return trivial_hash_func(p, hash_key) & 0xff;
+static void crippled_hashmap_func(const void *p, struct siphash *state) {
+ return trivial_hash_func(INT_TO_PTR(PTR_TO_INT(p) & 0xff), state);
}
static const struct hash_ops crippled_hashmap_ops = {
@@ -710,7 +712,7 @@ static void test_hashmap_many(void) {
unsigned n_entries;
} tests[] = {
{ .ops = NULL, .n_entries = 1 << 20 },
- { .ops = &crippled_hashmap_ops, .n_entries = 1 << 11 },
+ { .ops = &crippled_hashmap_ops, .n_entries = 1 << 14 },
};
diff --git a/src/test/test-hashmap.c b/src/test/test-hashmap.c
index d0e65001f5..83cea360e6 100644
--- a/src/test/test-hashmap.c
+++ b/src/test/test-hashmap.c
@@ -17,8 +17,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
#include "hashmap.h"
+#include "util.h"
void test_hashmap_funcs(void);
void test_ordered_hashmap_funcs(void);
diff --git a/src/test/test-helper.h b/src/test/test-helper.h
index f75dd3374a..c0f6a91787 100644
--- a/src/test/test-helper.h
+++ b/src/test/test-helper.h
@@ -23,9 +23,21 @@
#include "sd-daemon.h"
+#include "macro.h"
+
#define TEST_REQ_RUNNING_SYSTEMD(x) \
if (sd_booted() > 0) { \
x; \
} else { \
printf("systemd not booted skipping '%s'\n", #x); \
}
+
+#define MANAGER_SKIP_TEST(r) \
+ IN_SET(r, \
+ -EPERM, \
+ -EACCES, \
+ -EADDRINUSE, \
+ -EHOSTDOWN, \
+ -ENOENT, \
+ -ENOMEDIUM /* cannot determine cgroup */ \
+ )
diff --git a/src/test/test-hostname-util.c b/src/test/test-hostname-util.c
index 6f5ef2615e..590175433c 100644
--- a/src/test/test-hostname-util.c
+++ b/src/test/test-hostname-util.c
@@ -21,9 +21,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
+#include "alloc-util.h"
#include "fileio.h"
#include "hostname-util.h"
+#include "string-util.h"
+#include "util.h"
static void test_hostname_is_valid(void) {
assert_se(hostname_is_valid("foobar", false));
diff --git a/src/test/test-id128.c b/src/test/test-id128.c
index a6a0cd77a1..32cf3f80ca 100644
--- a/src/test/test-id128.c
+++ b/src/test/test-id128.c
@@ -21,11 +21,13 @@
#include <string.h>
-#include "systemd/sd-id128.h"
+#include "sd-daemon.h"
+#include "sd-id128.h"
-#include "util.h"
+#include "alloc-util.h"
#include "macro.h"
-#include "sd-daemon.h"
+#include "string-util.h"
+#include "util.h"
#define ID128_WALDI SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10)
#define STR_WALDI "0102030405060708090a0b0c0d0e0f10"
diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c
new file mode 100644
index 0000000000..08fde94f7f
--- /dev/null
+++ b/src/test/test-install-root.c
@@ -0,0 +1,665 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2011 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "alloc-util.h"
+#include "fileio.h"
+#include "install.h"
+#include "mkdir.h"
+#include "rm-rf.h"
+#include "string-util.h"
+
+static void test_basic_mask_and_enable(const char *root) {
+ const char *p;
+ UnitFileState state;
+ UnitFileChange *changes = NULL;
+ unsigned n_changes = 0;
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", NULL) == -ENOENT);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", NULL) == -ENOENT);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", NULL) == -ENOENT);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", NULL) == -ENOENT);
+
+ p = strjoina(root, "/usr/lib/systemd/system/a.service");
+ assert_se(write_string_file(p,
+ "[Install]\n"
+ "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", NULL) >= 0);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+
+ p = strjoina(root, "/usr/lib/systemd/system/b.service");
+ assert_se(symlink("a.service", p) >= 0);
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", NULL) >= 0);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+
+ p = strjoina(root, "/usr/lib/systemd/system/c.service");
+ assert_se(symlink("/usr/lib/systemd/system/a.service", p) >= 0);
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", NULL) >= 0);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+
+ p = strjoina(root, "/usr/lib/systemd/system/d.service");
+ assert_se(symlink("c.service", p) >= 0);
+
+ /* This one is interesting, as d follows a relative, then an absolute symlink */
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", NULL) >= 0);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+
+ assert_se(unit_file_mask(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), false, &changes, &n_changes) >= 0);
+ assert_se(n_changes == 1);
+ assert_se(changes[0].type == UNIT_FILE_SYMLINK);
+ assert_se(streq(changes[0].source, "/dev/null"));
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/a.service");
+ assert_se(streq(changes[0].path, p));
+
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_MASKED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_MASKED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_MASKED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_MASKED);
+
+ /* Enabling a masked unit should fail! */
+ assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), false, &changes, &n_changes) == -ESHUTDOWN);
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_unmask(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0);
+ assert_se(n_changes == 1);
+ assert_se(changes[0].type == UNIT_FILE_UNLINK);
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/a.service");
+ assert_se(streq(changes[0].path, p));
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), false, &changes, &n_changes) == 1);
+ assert_se(n_changes == 1);
+ assert_se(changes[0].type == UNIT_FILE_SYMLINK);
+ assert_se(streq(changes[0].source, "/usr/lib/systemd/system/a.service"));
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/a.service");
+ assert_se(streq(changes[0].path, p));
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+
+ /* Enabling it again should succeed but be a NOP */
+ assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), false, &changes, &n_changes) == 1);
+ assert_se(n_changes == 0);
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0);
+ assert_se(n_changes == 1);
+ assert_se(changes[0].type == UNIT_FILE_UNLINK);
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/a.service");
+ assert_se(streq(changes[0].path, p));
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+
+ /* Disabling a disabled unit must suceed but be a NOP */
+ assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0);
+ assert_se(n_changes == 0);
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ /* Let's enable this indirectly via a symlink */
+ assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("d.service"), false, &changes, &n_changes) >= 0);
+ assert_se(n_changes == 1);
+ assert_se(changes[0].type == UNIT_FILE_SYMLINK);
+ assert_se(streq(changes[0].source, "/usr/lib/systemd/system/a.service"));
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/a.service");
+ assert_se(streq(changes[0].path, p));
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+
+ /* Let's try to reenable */
+
+ assert_se(unit_file_reenable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("b.service"), false, &changes, &n_changes) >= 0);
+ assert_se(n_changes == 2);
+ assert_se(changes[0].type == UNIT_FILE_UNLINK);
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/a.service");
+ assert_se(streq(changes[0].path, p));
+ assert_se(changes[1].type == UNIT_FILE_SYMLINK);
+ assert_se(streq(changes[1].source, "/usr/lib/systemd/system/a.service"));
+ assert_se(streq(changes[1].path, p));
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+}
+
+static void test_linked_units(const char *root) {
+ const char *p, *q;
+ UnitFileState state;
+ UnitFileChange *changes = NULL;
+ unsigned n_changes = 0, i;
+
+ /*
+ * We'll test three cases here:
+ *
+ * a) a unit file in /opt, that we use "systemctl link" and
+ * "systemctl enable" on to make it available to the system
+ *
+ * b) a unit file in /opt, that is statically linked into
+ * /usr/lib/systemd/system, that "enable" should work on
+ * correctly.
+ *
+ * c) a unit file in /opt, that is linked into
+ * /etc/systemd/system, and where "enable" should result in
+ * -ELOOP, since using information from /etc to generate
+ * information in /etc should not be allowed.
+ */
+
+ p = strjoina(root, "/opt/linked.service");
+ assert_se(write_string_file(p,
+ "[Install]\n"
+ "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
+
+ p = strjoina(root, "/opt/linked2.service");
+ assert_se(write_string_file(p,
+ "[Install]\n"
+ "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
+
+ p = strjoina(root, "/opt/linked3.service");
+ assert_se(write_string_file(p,
+ "[Install]\n"
+ "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", NULL) == -ENOENT);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked2.service", NULL) == -ENOENT);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked3.service", NULL) == -ENOENT);
+
+ p = strjoina(root, "/usr/lib/systemd/system/linked2.service");
+ assert_se(symlink("/opt/linked2.service", p) >= 0);
+
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked3.service");
+ assert_se(symlink("/opt/linked3.service", p) >= 0);
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", &state) == -ENOENT);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked2.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked3.service", &state) >= 0 && state == UNIT_FILE_LINKED);
+
+ /* First, let's link the unit into the search path */
+ assert_se(unit_file_link(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("/opt/linked.service"), false, &changes, &n_changes) >= 0);
+ assert_se(n_changes == 1);
+ assert_se(changes[0].type == UNIT_FILE_SYMLINK);
+ assert_se(streq(changes[0].source, "/opt/linked.service"));
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked.service");
+ assert_se(streq(changes[0].path, p));
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", &state) >= 0 && state == UNIT_FILE_LINKED);
+
+ /* Let's unlink it from the search path again */
+ assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("linked.service"), &changes, &n_changes) >= 0);
+ assert_se(n_changes == 1);
+ assert_se(changes[0].type == UNIT_FILE_UNLINK);
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked.service");
+ assert_se(streq(changes[0].path, p));
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", NULL) == -ENOENT);
+
+ /* Now, let's not just link it, but also enable it */
+ assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("/opt/linked.service"), false, &changes, &n_changes) >= 0);
+ assert_se(n_changes == 2);
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/linked.service");
+ q = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked.service");
+ for (i = 0 ; i < n_changes; i++) {
+ assert_se(changes[i].type == UNIT_FILE_SYMLINK);
+ assert_se(streq(changes[i].source, "/opt/linked.service"));
+
+ if (p && streq(changes[i].path, p))
+ p = NULL;
+ else if (q && streq(changes[i].path, q))
+ q = NULL;
+ else
+ assert_not_reached("wut?");
+ }
+ assert(!p && !q);
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+
+ /* And let's unlink it again */
+ assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("linked.service"), &changes, &n_changes) >= 0);
+ assert_se(n_changes == 2);
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/linked.service");
+ q = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked.service");
+ for (i = 0; i < n_changes; i++) {
+ assert_se(changes[i].type == UNIT_FILE_UNLINK);
+
+ if (p && streq(changes[i].path, p))
+ p = NULL;
+ else if (q && streq(changes[i].path, q))
+ q = NULL;
+ else
+ assert_not_reached("wut?");
+ }
+ assert(!p && !q);
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", NULL) == -ENOENT);
+
+ assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("linked2.service"), false, &changes, &n_changes) >= 0);
+ assert_se(n_changes == 2);
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/linked2.service");
+ q = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked2.service");
+ for (i = 0 ; i < n_changes; i++) {
+ assert_se(changes[i].type == UNIT_FILE_SYMLINK);
+ assert_se(streq(changes[i].source, "/opt/linked2.service"));
+
+ if (p && streq(changes[i].path, p))
+ p = NULL;
+ else if (q && streq(changes[i].path, q))
+ q = NULL;
+ else
+ assert_not_reached("wut?");
+ }
+ assert(!p && !q);
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("linked3.service"), false, &changes, &n_changes) == -ELOOP);
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+}
+
+static void test_default(const char *root) {
+ _cleanup_free_ char *def = NULL;
+ UnitFileChange *changes = NULL;
+ unsigned n_changes = 0;
+ const char *p;
+
+ p = strjoina(root, "/usr/lib/systemd/system/test-default-real.target");
+ assert_se(write_string_file(p, "# pretty much empty", WRITE_STRING_FILE_CREATE) >= 0);
+
+ p = strjoina(root, "/usr/lib/systemd/system/test-default.target");
+ assert_se(symlink("test-default-real.target", p) >= 0);
+
+ assert_se(unit_file_get_default(UNIT_FILE_SYSTEM, root, &def) == -ENOENT);
+
+ assert_se(unit_file_set_default(UNIT_FILE_SYSTEM, root, "idontexist.target", false, &changes, &n_changes) == -ENOENT);
+ assert_se(n_changes == 0);
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_get_default(UNIT_FILE_SYSTEM, root, &def) == -ENOENT);
+
+ assert_se(unit_file_set_default(UNIT_FILE_SYSTEM, root, "test-default.target", false, &changes, &n_changes) >= 0);
+ assert_se(n_changes == 1);
+ assert_se(changes[0].type == UNIT_FILE_SYMLINK);
+ assert_se(streq(changes[0].source, "/usr/lib/systemd/system/test-default-real.target"));
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/default.target");
+ assert_se(streq(changes[0].path, p));
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_get_default(UNIT_FILE_SYSTEM, root, &def) >= 0);
+ assert_se(streq_ptr(def, "test-default-real.target"));
+}
+
+static void test_add_dependency(const char *root) {
+ UnitFileChange *changes = NULL;
+ unsigned n_changes = 0;
+ const char *p;
+
+ p = strjoina(root, "/usr/lib/systemd/system/real-add-dependency-test-target.target");
+ assert_se(write_string_file(p, "# pretty much empty", WRITE_STRING_FILE_CREATE) >= 0);
+
+ p = strjoina(root, "/usr/lib/systemd/system/add-dependency-test-target.target");
+ assert_se(symlink("real-add-dependency-test-target.target", p) >= 0);
+
+ p = strjoina(root, "/usr/lib/systemd/system/real-add-dependency-test-service.service");
+ assert_se(write_string_file(p, "# pretty much empty", WRITE_STRING_FILE_CREATE) >= 0);
+
+ p = strjoina(root, "/usr/lib/systemd/system/add-dependency-test-service.service");
+ assert_se(symlink("real-add-dependency-test-service.service", p) >= 0);
+
+ assert_se(unit_file_add_dependency(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("add-dependency-test-service.service"), "add-dependency-test-target.target", UNIT_WANTS, false, &changes, &n_changes) >= 0);
+ assert_se(n_changes == 1);
+ assert_se(changes[0].type == UNIT_FILE_SYMLINK);
+ assert_se(streq(changes[0].source, "/usr/lib/systemd/system/real-add-dependency-test-service.service"));
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/real-add-dependency-test-target.target.wants/real-add-dependency-test-service.service");
+ assert_se(streq(changes[0].path, p));
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+}
+
+static void test_template_enable(const char *root) {
+ UnitFileChange *changes = NULL;
+ unsigned n_changes = 0;
+ UnitFileState state;
+ const char *p;
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) == -ENOENT);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) == -ENOENT);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) == -ENOENT);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) == -ENOENT);
+
+ p = strjoina(root, "/usr/lib/systemd/system/template@.service");
+ assert_se(write_string_file(p,
+ "[Install]\n"
+ "DefaultInstance=def\n"
+ "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
+
+ p = strjoina(root, "/usr/lib/systemd/system/template-symlink@.service");
+ assert_se(symlink("template@.service", p) >= 0);
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+
+ assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("template@.service"), false, &changes, &n_changes) >= 0);
+ assert_se(n_changes == 1);
+ assert_se(changes[0].type == UNIT_FILE_SYMLINK);
+ assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service"));
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/template@def.service");
+ assert_se(streq(changes[0].path, p));
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+
+ assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("template@.service"), &changes, &n_changes) >= 0);
+ assert_se(n_changes == 1);
+ assert_se(changes[0].type == UNIT_FILE_UNLINK);
+ assert_se(streq(changes[0].path, p));
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+
+ assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("template@foo.service"), false, &changes, &n_changes) >= 0);
+ assert_se(changes[0].type == UNIT_FILE_SYMLINK);
+ assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service"));
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/template@foo.service");
+ assert_se(streq(changes[0].path, p));
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+
+ assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("template@foo.service"), &changes, &n_changes) >= 0);
+ assert_se(n_changes == 1);
+ assert_se(changes[0].type == UNIT_FILE_UNLINK);
+ assert_se(streq(changes[0].path, p));
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@quux.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@quux.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+
+ assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("template-symlink@quux.service"), false, &changes, &n_changes) >= 0);
+ assert_se(changes[0].type == UNIT_FILE_SYMLINK);
+ assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service"));
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/template@quux.service");
+ assert_se(streq(changes[0].path, p));
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@quux.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@quux.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+}
+
+static void test_indirect(const char *root) {
+ UnitFileChange *changes = NULL;
+ unsigned n_changes = 0;
+ UnitFileState state;
+ const char *p;
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirecta.service", &state) == -ENOENT);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectb.service", &state) == -ENOENT);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectc.service", &state) == -ENOENT);
+
+ p = strjoina(root, "/usr/lib/systemd/system/indirecta.service");
+ assert_se(write_string_file(p,
+ "[Install]\n"
+ "Also=indirectb.service\n", WRITE_STRING_FILE_CREATE) >= 0);
+
+ p = strjoina(root, "/usr/lib/systemd/system/indirectb.service");
+ assert_se(write_string_file(p,
+ "[Install]\n"
+ "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
+
+ p = strjoina(root, "/usr/lib/systemd/system/indirectc.service");
+ assert_se(symlink("indirecta.service", p) >= 0);
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirecta.service", &state) >= 0 && state == UNIT_FILE_INDIRECT);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectb.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectc.service", &state) >= 0 && state == UNIT_FILE_INDIRECT);
+
+ assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("indirectc.service"), false, &changes, &n_changes) >= 0);
+ assert_se(n_changes == 1);
+ assert_se(changes[0].type == UNIT_FILE_SYMLINK);
+ assert_se(streq(changes[0].source, "/usr/lib/systemd/system/indirectb.service"));
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/indirectb.service");
+ assert_se(streq(changes[0].path, p));
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirecta.service", &state) >= 0 && state == UNIT_FILE_INDIRECT);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectb.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectc.service", &state) >= 0 && state == UNIT_FILE_INDIRECT);
+
+ assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("indirectc.service"), &changes, &n_changes) >= 0);
+ assert_se(n_changes == 1);
+ assert_se(changes[0].type == UNIT_FILE_UNLINK);
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/indirectb.service");
+ assert_se(streq(changes[0].path, p));
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+}
+
+static void test_preset_and_list(const char *root) {
+ UnitFileChange *changes = NULL;
+ unsigned n_changes = 0, i;
+ const char *p, *q;
+ UnitFileState state;
+ bool got_yes = false, got_no = false;
+ Iterator j;
+ UnitFileList *fl;
+ Hashmap *h;
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) == -ENOENT);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) == -ENOENT);
+
+ p = strjoina(root, "/usr/lib/systemd/system/preset-yes.service");
+ assert_se(write_string_file(p,
+ "[Install]\n"
+ "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
+
+ p = strjoina(root, "/usr/lib/systemd/system/preset-no.service");
+ assert_se(write_string_file(p,
+ "[Install]\n"
+ "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
+
+ p = strjoina(root, "/usr/lib/systemd/system-preset/test.preset");
+ assert_se(write_string_file(p,
+ "enable *-yes.*\n"
+ "disable *\n", WRITE_STRING_FILE_CREATE) >= 0);
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+
+ assert_se(unit_file_preset(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("preset-yes.service"), UNIT_FILE_PRESET_FULL, false, &changes, &n_changes) >= 0);
+ assert_se(n_changes == 1);
+ assert_se(changes[0].type == UNIT_FILE_SYMLINK);
+ assert_se(streq(changes[0].source, "/usr/lib/systemd/system/preset-yes.service"));
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/preset-yes.service");
+ assert_se(streq(changes[0].path, p));
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+
+ assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("preset-yes.service"), &changes, &n_changes) >= 0);
+ assert_se(n_changes == 1);
+ assert_se(changes[0].type == UNIT_FILE_UNLINK);
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/preset-yes.service");
+ assert_se(streq(changes[0].path, p));
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+
+ assert_se(unit_file_preset(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("preset-no.service"), UNIT_FILE_PRESET_FULL, false, &changes, &n_changes) >= 0);
+ assert_se(n_changes == 0);
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+
+ assert_se(unit_file_preset_all(UNIT_FILE_SYSTEM, false, root, UNIT_FILE_PRESET_FULL, false, &changes, &n_changes) >= 0);
+
+ assert_se(n_changes > 0);
+
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/preset-yes.service");
+
+ for (i = 0; i < n_changes; i++) {
+
+ if (changes[i].type == UNIT_FILE_SYMLINK) {
+ assert_se(streq(changes[i].source, "/usr/lib/systemd/system/preset-yes.service"));
+ assert_se(streq(changes[i].path, p));
+ } else
+ assert_se(changes[i].type == UNIT_FILE_UNLINK);
+ }
+
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+
+ assert_se(h = hashmap_new(&string_hash_ops));
+ assert_se(unit_file_get_list(UNIT_FILE_SYSTEM, root, h) >= 0);
+
+ p = strjoina(root, "/usr/lib/systemd/system/preset-yes.service");
+ q = strjoina(root, "/usr/lib/systemd/system/preset-no.service");
+
+ HASHMAP_FOREACH(fl, h, j) {
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, basename(fl->path), &state) >= 0);
+ assert_se(fl->state == state);
+
+ if (streq(fl->path, p)) {
+ got_yes = true;
+ assert_se(fl->state == UNIT_FILE_ENABLED);
+ } else if (streq(fl->path, q)) {
+ got_no = true;
+ assert_se(fl->state == UNIT_FILE_DISABLED);
+ } else
+ assert_se(IN_SET(fl->state, UNIT_FILE_DISABLED, UNIT_FILE_STATIC, UNIT_FILE_INDIRECT));
+ }
+
+ unit_file_list_free(h);
+
+ assert_se(got_yes && got_no);
+}
+
+int main(int argc, char *argv[]) {
+ char root[] = "/tmp/rootXXXXXX";
+ const char *p;
+
+ assert_se(mkdtemp(root));
+
+ p = strjoina(root, "/usr/lib/systemd/system/");
+ assert_se(mkdir_p(p, 0755) >= 0);
+
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/");
+ assert_se(mkdir_p(p, 0755) >= 0);
+
+ p = strjoina(root, "/run/systemd/system/");
+ assert_se(mkdir_p(p, 0755) >= 0);
+
+ p = strjoina(root, "/opt/");
+ assert_se(mkdir_p(p, 0755) >= 0);
+
+ p = strjoina(root, "/usr/lib/systemd/system-preset/");
+ assert_se(mkdir_p(p, 0755) >= 0);
+
+ test_basic_mask_and_enable(root);
+ test_linked_units(root);
+ test_default(root);
+ test_add_dependency(root);
+ test_template_enable(root);
+ test_indirect(root);
+ test_preset_and_list(root);
+
+ assert_se(rm_rf(root, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
+
+ return 0;
+}
diff --git a/src/test/test-install.c b/src/test/test-install.c
index 5ee52e64cb..ef6f1efb89 100644
--- a/src/test/test-install.c
+++ b/src/test/test-install.c
@@ -19,8 +19,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <string.h>
#include <stdio.h>
+#include <string.h>
#include "install.h"
@@ -46,17 +46,19 @@ int main(int argc, char* argv[]) {
const char *const files2[] = { "/home/lennart/test.service", NULL };
UnitFileChange *changes = NULL;
unsigned n_changes = 0;
+ UnitFileState state = 0;
h = hashmap_new(&string_hash_ops);
r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h);
assert_se(r == 0);
HASHMAP_FOREACH(p, h, i) {
- UnitFileState s;
+ UnitFileState s = _UNIT_FILE_STATE_INVALID;
- s = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(p->path));
+ r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(p->path), &s);
- assert_se(p->state == s);
+ assert_se((r < 0 && p->state == UNIT_FILE_BAD) ||
+ (p->state == s));
fprintf(stderr, "%s (%s)\n",
p->path,
@@ -78,7 +80,9 @@ int main(int argc, char* argv[]) {
dump_changes(changes, n_changes);
unit_file_changes_free(changes, n_changes);
- assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_ENABLED);
+ r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state);
+ assert_se(r >= 0);
+ assert_se(state == UNIT_FILE_ENABLED);
log_error("disable");
@@ -91,7 +95,9 @@ int main(int argc, char* argv[]) {
dump_changes(changes, n_changes);
unit_file_changes_free(changes, n_changes);
- assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_DISABLED);
+ r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state);
+ assert_se(r >= 0);
+ assert_se(state == UNIT_FILE_DISABLED);
log_error("mask");
changes = NULL;
@@ -106,7 +112,9 @@ int main(int argc, char* argv[]) {
dump_changes(changes, n_changes);
unit_file_changes_free(changes, n_changes);
- assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_MASKED);
+ r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state);
+ assert_se(r >= 0);
+ assert_se(state == UNIT_FILE_MASKED);
log_error("unmask");
changes = NULL;
@@ -121,7 +129,9 @@ int main(int argc, char* argv[]) {
dump_changes(changes, n_changes);
unit_file_changes_free(changes, n_changes);
- assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_DISABLED);
+ r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state);
+ assert_se(r >= 0);
+ assert_se(state == UNIT_FILE_DISABLED);
log_error("mask");
changes = NULL;
@@ -133,7 +143,9 @@ int main(int argc, char* argv[]) {
dump_changes(changes, n_changes);
unit_file_changes_free(changes, n_changes);
- assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_MASKED);
+ r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state);
+ assert_se(r >= 0);
+ assert_se(state == UNIT_FILE_MASKED);
log_error("disable");
changes = NULL;
@@ -148,7 +160,9 @@ int main(int argc, char* argv[]) {
dump_changes(changes, n_changes);
unit_file_changes_free(changes, n_changes);
- assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_MASKED);
+ r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state);
+ assert_se(r >= 0);
+ assert_se(state == UNIT_FILE_MASKED);
log_error("umask");
changes = NULL;
@@ -160,7 +174,9 @@ int main(int argc, char* argv[]) {
dump_changes(changes, n_changes);
unit_file_changes_free(changes, n_changes);
- assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_DISABLED);
+ r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state);
+ assert_se(r >= 0);
+ assert_se(state == UNIT_FILE_DISABLED);
log_error("enable files2");
changes = NULL;
@@ -172,19 +188,22 @@ int main(int argc, char* argv[]) {
dump_changes(changes, n_changes);
unit_file_changes_free(changes, n_changes);
- assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == UNIT_FILE_ENABLED);
+ r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state);
+ assert_se(r >= 0);
+ assert_se(state == UNIT_FILE_ENABLED);
log_error("disable files2");
changes = NULL;
n_changes = 0;
- r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, (char**) files2, &changes, &n_changes);
+ r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, STRV_MAKE(basename(files2[0])), &changes, &n_changes);
assert_se(r >= 0);
dump_changes(changes, n_changes);
unit_file_changes_free(changes, n_changes);
- assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == _UNIT_FILE_STATE_INVALID);
+ r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state);
+ assert_se(r < 0);
log_error("link files2");
changes = NULL;
@@ -196,19 +215,22 @@ int main(int argc, char* argv[]) {
dump_changes(changes, n_changes);
unit_file_changes_free(changes, n_changes);
- assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == UNIT_FILE_LINKED);
+ r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state);
+ assert_se(r >= 0);
+ assert_se(state == UNIT_FILE_LINKED);
log_error("disable files2");
changes = NULL;
n_changes = 0;
- r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, (char**) files2, &changes, &n_changes);
+ r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, STRV_MAKE(basename(files2[0])), &changes, &n_changes);
assert_se(r >= 0);
dump_changes(changes, n_changes);
unit_file_changes_free(changes, n_changes);
- assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == _UNIT_FILE_STATE_INVALID);
+ r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state);
+ assert_se(r < 0);
log_error("link files2");
changes = NULL;
@@ -220,7 +242,9 @@ int main(int argc, char* argv[]) {
dump_changes(changes, n_changes);
unit_file_changes_free(changes, n_changes);
- assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == UNIT_FILE_LINKED);
+ r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state);
+ assert_se(r >= 0);
+ assert_se(state == UNIT_FILE_LINKED);
log_error("reenable files2");
changes = NULL;
@@ -232,19 +256,22 @@ int main(int argc, char* argv[]) {
dump_changes(changes, n_changes);
unit_file_changes_free(changes, n_changes);
- assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == UNIT_FILE_ENABLED);
+ r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state);
+ assert_se(r >= 0);
+ assert_se(state == UNIT_FILE_ENABLED);
log_error("disable files2");
changes = NULL;
n_changes = 0;
- r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, (char**) files2, &changes, &n_changes);
+ r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, STRV_MAKE(basename(files2[0])), &changes, &n_changes);
assert_se(r >= 0);
dump_changes(changes, n_changes);
unit_file_changes_free(changes, n_changes);
- assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == _UNIT_FILE_STATE_INVALID);
+ r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state);
+ assert_se(r < 0);
log_error("preset files");
changes = NULL;
n_changes = 0;
@@ -255,7 +282,9 @@ int main(int argc, char* argv[]) {
dump_changes(changes, n_changes);
unit_file_changes_free(changes, n_changes);
- assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files[0])) == UNIT_FILE_ENABLED);
+ r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files[0]), &state);
+ assert_se(r >= 0);
+ assert_se(state == UNIT_FILE_ENABLED);
return 0;
}
diff --git a/src/test/test-ipcrm.c b/src/test/test-ipcrm.c
index 4944bf6ad9..5841cb3fb1 100644
--- a/src/test/test-ipcrm.c
+++ b/src/test/test-ipcrm.c
@@ -19,8 +19,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
#include "clean-ipc.h"
+#include "user-util.h"
+#include "util.h"
int main(int argc, char *argv[]) {
uid_t uid;
diff --git a/src/test/test-job-type.c b/src/test/test-job-type.c
index af0d76e894..75ce3a349e 100644
--- a/src/test/test-job-type.c
+++ b/src/test/test-job-type.c
@@ -22,8 +22,8 @@
#include <stdio.h>
#include "job.h"
-#include "unit.h"
#include "service.h"
+#include "unit.h"
int main(int argc, char*argv[]) {
JobType a, b, c, ab, bc, ab_c, bc_a, a_bc;
diff --git a/src/test/test-json.c b/src/test/test-json.c
index 1058c583c3..3995224eea 100644
--- a/src/test/test-json.c
+++ b/src/test/test-json.c
@@ -21,8 +21,10 @@
#include <math.h>
-#include "util.h"
+#include "alloc-util.h"
#include "json.h"
+#include "string-util.h"
+#include "util.h"
static void test_one(const char *data, ...) {
void *state = NULL;
diff --git a/src/test/test-libudev.c b/src/test/test-libudev.c
index 34c49b969a..94d852b3b0 100644
--- a/src/test/test-libudev.c
+++ b/src/test/test-libudev.c
@@ -18,12 +18,15 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
-#include <unistd.h>
#include <getopt.h>
+#include <stdio.h>
#include <sys/epoll.h>
+#include <unistd.h>
#include "libudev.h"
+
+#include "stdio-util.h"
+#include "string-util.h"
#include "udev-util.h"
#include "util.h"
@@ -458,7 +461,7 @@ int main(int argc, char *argv[]) {
/* add sys path if needed */
if (!startswith(syspath, "/sys")) {
- snprintf(path, sizeof(path), "/sys/%s", syspath);
+ xsprintf(path, "/sys/%s", syspath);
syspath = path;
}
diff --git a/src/test/test-locale-util.c b/src/test/test-locale-util.c
index 9765075365..427c698d1d 100644
--- a/src/test/test-locale-util.c
+++ b/src/test/test-locale-util.c
@@ -19,8 +19,8 @@
#include "locale-util.h"
-#include "strv.h"
#include "macro.h"
+#include "strv.h"
static void test_get_locales(void) {
_cleanup_strv_free_ char **locales = NULL;
diff --git a/src/test/test-log.c b/src/test/test-log.c
index 9dcfa2f274..a01df9b049 100644
--- a/src/test/test-log.c
+++ b/src/test/test-log.c
@@ -22,9 +22,9 @@
#include <stddef.h>
#include <unistd.h>
+#include "formats-util.h"
#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 e3e5a95add..556938a0f8 100644
--- a/src/test/test-loopback.c
+++ b/src/test/test-loopback.c
@@ -19,11 +19,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <string.h>
#include <stdio.h>
+#include <string.h>
-#include "loopback-setup.h"
#include "log.h"
+#include "loopback-setup.h"
int main(int argc, char* argv[]) {
int r;
diff --git a/src/test/test-namespace.c b/src/test/test-namespace.c
index 7d7e08dc5d..5a12e959d4 100644
--- a/src/test/test-namespace.c
+++ b/src/test/test-namespace.c
@@ -21,9 +21,12 @@
#include <sys/socket.h>
+#include "alloc-util.h"
+#include "fd-util.h"
#include "namespace.h"
-#include "util.h"
#include "process-util.h"
+#include "string-util.h"
+#include "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-netlink-manual.c b/src/test/test-netlink-manual.c
index 2879d7450f..a1e8774063 100644
--- a/src/test/test-netlink-manual.c
+++ b/src/test/test-netlink-manual.c
@@ -20,15 +20,16 @@
***/
#include <arpa/inet.h>
-#include <net/if.h>
+#include <libkmod.h>
#include <linux/ip.h>
+#include <net/if.h>
#include <linux/if_tunnel.h>
-#include <libkmod.h>
-#include "util.h"
-#include "macro.h"
#include "sd-netlink.h"
+#include "macro.h"
+#include "util.h"
+
static int load_module(const char *mod_name) {
struct kmod_ctx *ctx;
struct kmod_list *list = NULL, *l;
diff --git a/src/test/test-ns.c b/src/test/test-ns.c
index 3050be9e9d..1175114a3a 100644
--- a/src/test/test-ns.c
+++ b/src/test/test-ns.c
@@ -22,8 +22,8 @@
#include <stdlib.h>
#include <unistd.h>
-#include "namespace.h"
#include "log.h"
+#include "namespace.h"
int main(int argc, char *argv[]) {
const char * const writable[] = {
diff --git a/src/test/test-parse-util.c b/src/test/test-parse-util.c
new file mode 100644
index 0000000000..f0d5d71083
--- /dev/null
+++ b/src/test/test-parse-util.c
@@ -0,0 +1,495 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+ Copyright 2013 Thomas H.P. Andersen
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <locale.h>
+#include <math.h>
+
+#include "log.h"
+#include "parse-util.h"
+
+static void test_parse_boolean(void) {
+ assert_se(parse_boolean("1") == 1);
+ assert_se(parse_boolean("y") == 1);
+ assert_se(parse_boolean("Y") == 1);
+ assert_se(parse_boolean("yes") == 1);
+ assert_se(parse_boolean("YES") == 1);
+ assert_se(parse_boolean("true") == 1);
+ assert_se(parse_boolean("TRUE") == 1);
+ assert_se(parse_boolean("on") == 1);
+ assert_se(parse_boolean("ON") == 1);
+
+ assert_se(parse_boolean("0") == 0);
+ assert_se(parse_boolean("n") == 0);
+ assert_se(parse_boolean("N") == 0);
+ assert_se(parse_boolean("no") == 0);
+ assert_se(parse_boolean("NO") == 0);
+ assert_se(parse_boolean("false") == 0);
+ assert_se(parse_boolean("FALSE") == 0);
+ assert_se(parse_boolean("off") == 0);
+ assert_se(parse_boolean("OFF") == 0);
+
+ assert_se(parse_boolean("garbage") < 0);
+ assert_se(parse_boolean("") < 0);
+ assert_se(parse_boolean("full") < 0);
+}
+
+static void test_parse_pid(void) {
+ int r;
+ pid_t pid;
+
+ r = parse_pid("100", &pid);
+ assert_se(r == 0);
+ assert_se(pid == 100);
+
+ r = parse_pid("0x7FFFFFFF", &pid);
+ assert_se(r == 0);
+ assert_se(pid == 2147483647);
+
+ pid = 65; /* pid is left unchanged on ERANGE. Set to known arbitrary value. */
+ r = parse_pid("0", &pid);
+ assert_se(r == -ERANGE);
+ assert_se(pid == 65);
+
+ pid = 65; /* pid is left unchanged on ERANGE. Set to known arbitrary value. */
+ r = parse_pid("-100", &pid);
+ assert_se(r == -ERANGE);
+ assert_se(pid == 65);
+
+ pid = 65; /* pid is left unchanged on ERANGE. Set to known arbitrary value. */
+ r = parse_pid("0xFFFFFFFFFFFFFFFFF", &pid);
+ assert_se(r == -ERANGE);
+ assert_se(pid == 65);
+
+ r = parse_pid("junk", &pid);
+ assert_se(r == -EINVAL);
+}
+
+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);
+}
+
+static void test_parse_size(void) {
+ uint64_t bytes;
+
+ assert_se(parse_size("111", 1024, &bytes) == 0);
+ assert_se(bytes == 111);
+
+ assert_se(parse_size("111.4", 1024, &bytes) == 0);
+ assert_se(bytes == 111);
+
+ assert_se(parse_size(" 112 B", 1024, &bytes) == 0);
+ assert_se(bytes == 112);
+
+ assert_se(parse_size(" 112.6 B", 1024, &bytes) == 0);
+ assert_se(bytes == 112);
+
+ assert_se(parse_size("3.5 K", 1024, &bytes) == 0);
+ assert_se(bytes == 3*1024 + 512);
+
+ assert_se(parse_size("3. K", 1024, &bytes) == 0);
+ assert_se(bytes == 3*1024);
+
+ assert_se(parse_size("3.0 K", 1024, &bytes) == 0);
+ assert_se(bytes == 3*1024);
+
+ assert_se(parse_size("3. 0 K", 1024, &bytes) == -EINVAL);
+
+ assert_se(parse_size(" 4 M 11.5K", 1024, &bytes) == 0);
+ assert_se(bytes == 4*1024*1024 + 11 * 1024 + 512);
+
+ assert_se(parse_size("3B3.5G", 1024, &bytes) == -EINVAL);
+
+ assert_se(parse_size("3.5G3B", 1024, &bytes) == 0);
+ assert_se(bytes == 3ULL*1024*1024*1024 + 512*1024*1024 + 3);
+
+ assert_se(parse_size("3.5G 4B", 1024, &bytes) == 0);
+ assert_se(bytes == 3ULL*1024*1024*1024 + 512*1024*1024 + 4);
+
+ assert_se(parse_size("3B3G4T", 1024, &bytes) == -EINVAL);
+
+ assert_se(parse_size("4T3G3B", 1024, &bytes) == 0);
+ assert_se(bytes == (4ULL*1024 + 3)*1024*1024*1024 + 3);
+
+ assert_se(parse_size(" 4 T 3 G 3 B", 1024, &bytes) == 0);
+ assert_se(bytes == (4ULL*1024 + 3)*1024*1024*1024 + 3);
+
+ assert_se(parse_size("12P", 1024, &bytes) == 0);
+ assert_se(bytes == 12ULL * 1024*1024*1024*1024*1024);
+
+ assert_se(parse_size("12P12P", 1024, &bytes) == -EINVAL);
+
+ assert_se(parse_size("3E 2P", 1024, &bytes) == 0);
+ assert_se(bytes == (3 * 1024 + 2ULL) * 1024*1024*1024*1024*1024);
+
+ assert_se(parse_size("12X", 1024, &bytes) == -EINVAL);
+
+ assert_se(parse_size("12.5X", 1024, &bytes) == -EINVAL);
+
+ assert_se(parse_size("12.5e3", 1024, &bytes) == -EINVAL);
+
+ assert_se(parse_size("1024E", 1024, &bytes) == -ERANGE);
+ assert_se(parse_size("-1", 1024, &bytes) == -ERANGE);
+ assert_se(parse_size("-1024E", 1024, &bytes) == -ERANGE);
+
+ assert_se(parse_size("-1024P", 1024, &bytes) == -ERANGE);
+
+ assert_se(parse_size("-10B 20K", 1024, &bytes) == -ERANGE);
+}
+
+static void test_parse_range(void) {
+ unsigned lower, upper;
+
+ /* Successful cases */
+ assert_se(parse_range("111", &lower, &upper) == 0);
+ assert_se(lower == 111);
+ assert_se(upper == 111);
+
+ assert_se(parse_range("111-123", &lower, &upper) == 0);
+ assert_se(lower == 111);
+ assert_se(upper == 123);
+
+ assert_se(parse_range("123-111", &lower, &upper) == 0);
+ assert_se(lower == 123);
+ assert_se(upper == 111);
+
+ assert_se(parse_range("123-123", &lower, &upper) == 0);
+ assert_se(lower == 123);
+ assert_se(upper == 123);
+
+ assert_se(parse_range("0", &lower, &upper) == 0);
+ assert_se(lower == 0);
+ assert_se(upper == 0);
+
+ assert_se(parse_range("0-15", &lower, &upper) == 0);
+ assert_se(lower == 0);
+ assert_se(upper == 15);
+
+ assert_se(parse_range("15-0", &lower, &upper) == 0);
+ assert_se(lower == 15);
+ assert_se(upper == 0);
+
+ assert_se(parse_range("128-65535", &lower, &upper) == 0);
+ assert_se(lower == 128);
+ assert_se(upper == 65535);
+
+ assert_se(parse_range("1024-4294967295", &lower, &upper) == 0);
+ assert_se(lower == 1024);
+ assert_se(upper == 4294967295);
+
+ /* Leading whitespace is acceptable */
+ assert_se(parse_range(" 111", &lower, &upper) == 0);
+ assert_se(lower == 111);
+ assert_se(upper == 111);
+
+ assert_se(parse_range(" 111-123", &lower, &upper) == 0);
+ assert_se(lower == 111);
+ assert_se(upper == 123);
+
+ assert_se(parse_range("111- 123", &lower, &upper) == 0);
+ assert_se(lower == 111);
+ assert_se(upper == 123);
+
+ assert_se(parse_range("\t111-\t123", &lower, &upper) == 0);
+ assert_se(lower == 111);
+ assert_se(upper == 123);
+
+ assert_se(parse_range(" \t 111- \t 123", &lower, &upper) == 0);
+ assert_se(lower == 111);
+ assert_se(upper == 123);
+
+ /* Error cases, make sure they fail as expected */
+ lower = upper = 9999;
+ assert_se(parse_range("111garbage", &lower, &upper) == -EINVAL);
+ assert_se(lower == 9999);
+ assert_se(upper == 9999);
+
+ assert_se(parse_range("garbage111", &lower, &upper) == -EINVAL);
+ assert_se(lower == 9999);
+ assert_se(upper == 9999);
+
+ assert_se(parse_range("garbage", &lower, &upper) == -EINVAL);
+ assert_se(lower == 9999);
+ assert_se(upper == 9999);
+
+ assert_se(parse_range("111-123garbage", &lower, &upper) == -EINVAL);
+ assert_se(lower == 9999);
+ assert_se(upper == 9999);
+
+ assert_se(parse_range("111garbage-123", &lower, &upper) == -EINVAL);
+ assert_se(lower == 9999);
+ assert_se(upper == 9999);
+
+ /* Empty string */
+ lower = upper = 9999;
+ assert_se(parse_range("", &lower, &upper) == -EINVAL);
+ assert_se(lower == 9999);
+ assert_se(upper == 9999);
+
+ /* 111--123 will pass -123 to safe_atou which returns -ERANGE for negative */
+ assert_se(parse_range("111--123", &lower, &upper) == -ERANGE);
+ assert_se(lower == 9999);
+ assert_se(upper == 9999);
+
+ assert_se(parse_range("-111-123", &lower, &upper) == -EINVAL);
+ assert_se(lower == 9999);
+ assert_se(upper == 9999);
+
+ assert_se(parse_range("111-123-", &lower, &upper) == -EINVAL);
+ assert_se(lower == 9999);
+ assert_se(upper == 9999);
+
+ assert_se(parse_range("111.4-123", &lower, &upper) == -EINVAL);
+ assert_se(lower == 9999);
+ assert_se(upper == 9999);
+
+ assert_se(parse_range("111-123.4", &lower, &upper) == -EINVAL);
+ assert_se(lower == 9999);
+ assert_se(upper == 9999);
+
+ assert_se(parse_range("111,4-123", &lower, &upper) == -EINVAL);
+ assert_se(lower == 9999);
+ assert_se(upper == 9999);
+
+ assert_se(parse_range("111-123,4", &lower, &upper) == -EINVAL);
+ assert_se(lower == 9999);
+ assert_se(upper == 9999);
+
+ /* Error on trailing dash */
+ assert_se(parse_range("111-", &lower, &upper) == -EINVAL);
+ assert_se(lower == 9999);
+ assert_se(upper == 9999);
+
+ assert_se(parse_range("111-123-", &lower, &upper) == -EINVAL);
+ assert_se(lower == 9999);
+ assert_se(upper == 9999);
+
+ assert_se(parse_range("111--", &lower, &upper) == -EINVAL);
+ assert_se(lower == 9999);
+ assert_se(upper == 9999);
+
+ assert_se(parse_range("111- ", &lower, &upper) == -EINVAL);
+ assert_se(lower == 9999);
+ assert_se(upper == 9999);
+
+ /* Whitespace is not a separator */
+ assert_se(parse_range("111 123", &lower, &upper) == -EINVAL);
+ assert_se(lower == 9999);
+ assert_se(upper == 9999);
+
+ assert_se(parse_range("111\t123", &lower, &upper) == -EINVAL);
+ assert_se(lower == 9999);
+ assert_se(upper == 9999);
+
+ assert_se(parse_range("111 \t 123", &lower, &upper) == -EINVAL);
+ assert_se(lower == 9999);
+ assert_se(upper == 9999);
+
+ /* Trailing whitespace is invalid (from safe_atou) */
+ assert_se(parse_range("111 ", &lower, &upper) == -EINVAL);
+ assert_se(lower == 9999);
+ assert_se(upper == 9999);
+
+ assert_se(parse_range("111-123 ", &lower, &upper) == -EINVAL);
+ assert_se(lower == 9999);
+ assert_se(upper == 9999);
+
+ assert_se(parse_range("111 -123", &lower, &upper) == -EINVAL);
+ assert_se(lower == 9999);
+ assert_se(upper == 9999);
+
+ assert_se(parse_range("111 -123 ", &lower, &upper) == -EINVAL);
+ assert_se(lower == 9999);
+ assert_se(upper == 9999);
+
+ assert_se(parse_range("111\t-123\t", &lower, &upper) == -EINVAL);
+ assert_se(lower == 9999);
+ assert_se(upper == 9999);
+
+ assert_se(parse_range("111 \t -123 \t ", &lower, &upper) == -EINVAL);
+ assert_se(lower == 9999);
+ assert_se(upper == 9999);
+
+ /* Out of the "unsigned" range, this is 1<<64 */
+ assert_se(parse_range("0-18446744073709551616", &lower, &upper) == -ERANGE);
+ assert_se(lower == 9999);
+ assert_se(upper == 9999);
+}
+
+static void test_safe_atolli(void) {
+ int r;
+ long long l;
+
+ r = safe_atolli("12345", &l);
+ assert_se(r == 0);
+ assert_se(l == 12345);
+
+ r = safe_atolli(" 12345", &l);
+ assert_se(r == 0);
+ assert_se(l == 12345);
+
+ r = safe_atolli("-12345", &l);
+ assert_se(r == 0);
+ assert_se(l == -12345);
+
+ r = safe_atolli(" -12345", &l);
+ assert_se(r == 0);
+ assert_se(l == -12345);
+
+ r = safe_atolli("12345678901234567890", &l);
+ assert_se(r == -ERANGE);
+
+ r = safe_atolli("-12345678901234567890", &l);
+ assert_se(r == -ERANGE);
+
+ r = safe_atolli("junk", &l);
+ assert_se(r == -EINVAL);
+}
+
+static void test_safe_atou16(void) {
+ int r;
+ uint16_t l;
+
+ r = safe_atou16("12345", &l);
+ assert_se(r == 0);
+ assert_se(l == 12345);
+
+ r = safe_atou16(" 12345", &l);
+ assert_se(r == 0);
+ assert_se(l == 12345);
+
+ r = safe_atou16("123456", &l);
+ assert_se(r == -ERANGE);
+
+ r = safe_atou16("-1", &l);
+ assert_se(r == -ERANGE);
+
+ r = safe_atou16(" -1", &l);
+ assert_se(r == -ERANGE);
+
+ r = safe_atou16("junk", &l);
+ assert_se(r == -EINVAL);
+}
+
+static void test_safe_atoi16(void) {
+ int r;
+ int16_t l;
+
+ r = safe_atoi16("-12345", &l);
+ assert_se(r == 0);
+ assert_se(l == -12345);
+
+ r = safe_atoi16(" -12345", &l);
+ assert_se(r == 0);
+ assert_se(l == -12345);
+
+ r = safe_atoi16("32767", &l);
+ assert_se(r == 0);
+ assert_se(l == 32767);
+
+ r = safe_atoi16(" 32767", &l);
+ assert_se(r == 0);
+ assert_se(l == 32767);
+
+ r = safe_atoi16("36536", &l);
+ assert_se(r == -ERANGE);
+
+ r = safe_atoi16("-32769", &l);
+ assert_se(r == -ERANGE);
+
+ r = safe_atoi16("junk", &l);
+ assert_se(r == -EINVAL);
+}
+
+static void test_safe_atod(void) {
+ int r;
+ double d;
+ char *e;
+
+ r = safe_atod("junk", &d);
+ assert_se(r == -EINVAL);
+
+ r = safe_atod("0.2244", &d);
+ assert_se(r == 0);
+ assert_se(fabs(d - 0.2244) < 0.000001);
+
+ r = safe_atod("0,5", &d);
+ assert_se(r == -EINVAL);
+
+ errno = 0;
+ strtod("0,5", &e);
+ assert_se(*e == ',');
+
+ /* Check if this really is locale independent */
+ if (setlocale(LC_NUMERIC, "de_DE.utf8")) {
+
+ r = safe_atod("0.2244", &d);
+ assert_se(r == 0);
+ assert_se(fabs(d - 0.2244) < 0.000001);
+
+ r = safe_atod("0,5", &d);
+ assert_se(r == -EINVAL);
+
+ errno = 0;
+ assert_se(fabs(strtod("0,5", &e) - 0.5) < 0.00001);
+ }
+
+ /* And check again, reset */
+ assert_se(setlocale(LC_NUMERIC, "C"));
+
+ r = safe_atod("0.2244", &d);
+ assert_se(r == 0);
+ assert_se(fabs(d - 0.2244) < 0.000001);
+
+ r = safe_atod("0,5", &d);
+ assert_se(r == -EINVAL);
+
+ errno = 0;
+ strtod("0,5", &e);
+ assert_se(*e == ',');
+}
+
+int main(int argc, char *argv[]) {
+ log_parse_environment();
+ log_open();
+
+ test_parse_boolean();
+ test_parse_pid();
+ test_parse_mode();
+ test_parse_size();
+ test_parse_range();
+ test_safe_atolli();
+ test_safe_atou16();
+ test_safe_atoi16();
+ test_safe_atod();
+
+ return 0;
+}
diff --git a/src/test/test-path-lookup.c b/src/test/test-path-lookup.c
index aa4bac6cdd..65cb894ff7 100644
--- a/src/test/test-path-lookup.c
+++ b/src/test/test-path-lookup.c
@@ -19,29 +19,39 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <stdlib.h>
#include <sys/stat.h>
-#include "path-lookup.h"
#include "log.h"
-#include "strv.h"
+#include "path-lookup.h"
#include "rm-rf.h"
+#include "string-util.h"
+#include "strv.h"
static void test_paths(ManagerRunningAs running_as, bool personal) {
char template[] = "/tmp/test-path-lookup.XXXXXXX";
- _cleanup_lookup_paths_free_ LookupPaths lp = {};
- char *exists, *not;
+ _cleanup_lookup_paths_free_ LookupPaths lp_without_env = {};
+ _cleanup_lookup_paths_free_ LookupPaths lp_with_env = {};
+ char *exists, *not, *systemd_unit_path;
assert_se(mkdtemp(template));
exists = strjoina(template, "/exists");
assert_se(mkdir(exists, 0755) == 0);
not = strjoina(template, "/not");
- assert_se(lookup_paths_init(&lp, running_as, personal, NULL, exists, not, not) == 0);
+ assert_se(unsetenv("SYSTEMD_UNIT_PATH") == 0);
+ assert_se(lookup_paths_init(&lp_without_env, running_as, personal, NULL, exists, not, not) == 0);
+
+ assert_se(!strv_isempty(lp_without_env.unit_path));
+ assert_se(strv_contains(lp_without_env.unit_path, exists));
+ assert_se(strv_contains(lp_without_env.unit_path, not));
- assert_se(!strv_isempty(lp.unit_path));
- assert_se(strv_contains(lp.unit_path, exists));
- assert_se(strv_contains(lp.unit_path, not));
+ systemd_unit_path = strjoina(template, "/systemd-unit-path");
+ assert_se(setenv("SYSTEMD_UNIT_PATH", systemd_unit_path, 1) == 0);
+ assert_se(lookup_paths_init(&lp_with_env, running_as, personal, NULL, exists, not, not) == 0);
+ assert_se(strv_length(lp_with_env.unit_path) == 1);
+ assert_se(streq(lp_with_env.unit_path[0], systemd_unit_path));
assert_se(rm_rf(template, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}
diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c
index fce4e81a09..3f0f0264ab 100644
--- a/src/test/test-path-util.c
+++ b/src/test/test-path-util.c
@@ -20,14 +20,18 @@
***/
#include <stdio.h>
-#include <unistd.h>
#include <sys/mount.h>
+#include <unistd.h>
-#include "path-util.h"
-#include "util.h"
+#include "alloc-util.h"
+#include "fd-util.h"
#include "macro.h"
-#include "strv.h"
+#include "mount-util.h"
+#include "path-util.h"
#include "rm-rf.h"
+#include "string-util.h"
+#include "strv.h"
+#include "util.h"
#define test_path_compare(a, b, result) { \
assert_se(path_compare(a, b) == result); \
@@ -75,20 +79,6 @@ static void test_path(void) {
assert_se(streq(basename("/aa///file..."), "file..."));
assert_se(streq(basename("file.../"), ""));
-#define test_parent(x, y) { \
- _cleanup_free_ char *z = NULL; \
- int r = path_get_parent(x, &z); \
- printf("expected: %s\n", y ? y : "error"); \
- printf("actual: %s\n", r<0 ? "error" : z); \
- assert_se((y==NULL) ^ (r==0)); \
- assert_se(y==NULL || path_equal(z, y)); \
- }
-
- test_parent("./aa/bb/../file.da.", "./aa/bb/..");
- test_parent("/aa///.file", "/aa///");
- test_parent("/aa///file...", "/aa///");
- test_parent("file.../", NULL);
-
fd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY);
assert_se(fd >= 0);
assert_se(fd_is_mount_point(fd, "/", 0) > 0);
@@ -104,32 +94,28 @@ static void test_path(void) {
}
}
-static void test_find_binary(const char *self, bool local) {
+static void test_find_binary(const char *self) {
char *p;
- assert_se(find_binary("/bin/sh", local, &p) == 0);
+ assert_se(find_binary("/bin/sh", &p) == 0);
puts(p);
- assert_se(streq(p, "/bin/sh"));
+ assert_se(path_equal(p, "/bin/sh"));
free(p);
- assert_se(find_binary(self, local, &p) == 0);
+ assert_se(find_binary(self, &p) == 0);
puts(p);
assert_se(endswith(p, "/test-path-util"));
assert_se(path_is_absolute(p));
free(p);
- assert_se(find_binary("sh", local, &p) == 0);
+ assert_se(find_binary("sh", &p) == 0);
puts(p);
assert_se(endswith(p, "/sh"));
assert_se(path_is_absolute(p));
free(p);
- assert_se(find_binary("xxxx-xxxx", local, &p) == -ENOENT);
-
- assert_se(find_binary("/some/dir/xxxx-xxxx", local, &p) ==
- (local ? -ENOENT : 0));
- if (!local)
- free(p);
+ assert_se(find_binary("xxxx-xxxx", &p) == -ENOENT);
+ assert_se(find_binary("/some/dir/xxxx-xxxx", &p) == -ENOENT);
}
static void test_prefixes(void) {
@@ -210,9 +196,10 @@ static void test_fsck_exists(void) {
unsetenv("PATH");
/* fsck.minix is provided by util-linux and will probably exist. */
- assert_se(fsck_exists("minix") == 0);
+ assert_se(fsck_exists("minix") == 1);
- assert_se(fsck_exists("AbCdE") == -ENOENT);
+ assert_se(fsck_exists("AbCdE") == 0);
+ assert_se(fsck_exists("/../bin/") == 0);
}
static void test_make_relative(void) {
@@ -450,8 +437,7 @@ static void test_path_is_mount_point(void) {
int main(int argc, char **argv) {
test_path();
- test_find_binary(argv[0], true);
- test_find_binary(argv[0], false);
+ test_find_binary(argv[0]);
test_prefixes();
test_path_join();
test_fsck_exists();
diff --git a/src/test/test-path.c b/src/test/test-path.c
index 676c9f1793..7a3b145414 100644
--- a/src/test/test-path.c
+++ b/src/test/test-path.c
@@ -17,16 +17,21 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
#include <stdbool.h>
+#include <stdio.h>
-#include "unit.h"
-#include "manager.h"
-#include "util.h"
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fs-util.h"
#include "macro.h"
-#include "strv.h"
+#include "manager.h"
#include "mkdir.h"
#include "rm-rf.h"
+#include "string-util.h"
+#include "strv.h"
+#include "test-helper.h"
+#include "unit.h"
+#include "util.h"
typedef void (*test_function_t)(Manager *m);
@@ -40,8 +45,8 @@ static int setup_test(Manager **m) {
assert_se(m);
r = manager_new(MANAGER_USER, true, &tmp);
- if (IN_SET(r, -EPERM, -EACCES, -EADDRINUSE, -EHOSTDOWN, -ENOENT, -ENOEXEC)) {
- printf("Skipping test: manager_new: %s", strerror(-r));
+ if (MANAGER_SKIP_TEST(r)) {
+ printf("Skipping test: manager_new: %s\n", strerror(-r));
return -EXIT_TEST_SKIP;
}
assert_se(r >= 0);
@@ -254,7 +259,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 "/test-path/") >= 0);
for (test = tests; test && *test; test++) {
int r;
diff --git a/src/test/test-prioq.c b/src/test/test-prioq.c
index dfedc9b8dc..07273ffe79 100644
--- a/src/test/test-prioq.c
+++ b/src/test/test-prioq.c
@@ -21,10 +21,11 @@
#include <stdlib.h>
-#include "util.h"
-#include "set.h"
+#include "alloc-util.h"
#include "prioq.h"
+#include "set.h"
#include "siphash24.h"
+#include "util.h"
#define SET_SIZE 1024*4
@@ -89,13 +90,10 @@ static int test_compare(const void *a, const void *b) {
return 0;
}
-static unsigned long test_hash(const void *a, const uint8_t hash_key[HASH_KEY_SIZE]) {
+static void test_hash(const void *a, struct siphash *state) {
const struct test *x = a;
- uint64_t u;
-
- siphash24((uint8_t*) &u, &x->value, sizeof(x->value), hash_key);
- return (unsigned long) u;
+ siphash24_compress(&x->value, sizeof(x->value), state);
}
static const struct hash_ops test_hash_ops = {
diff --git a/src/test/test-process-util.c b/src/test/test-process-util.c
index eb0f443a43..48be5a3a87 100644
--- a/src/test/test-process-util.c
+++ b/src/test/test-process-util.c
@@ -18,17 +18,19 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
-#include "process-util.h"
+#include "alloc-util.h"
#include "log.h"
-#include "util.h"
#include "macro.h"
-#include "virt.h"
+#include "process-util.h"
+#include "string-util.h"
#include "terminal-util.h"
+#include "util.h"
+#include "virt.h"
static void test_get_process_comm(void) {
struct stat st;
@@ -53,7 +55,7 @@ static void test_get_process_comm(void) {
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);
+ assert_se(get_process_ppid(1, &e) >= 0);
log_info("pid1 ppid: "PID_FMT, e);
assert_se(e == 0);
diff --git a/src/test/test-ratelimit.c b/src/test/test-ratelimit.c
index 462b55cdb3..990b834c79 100644
--- a/src/test/test-ratelimit.c
+++ b/src/test/test-ratelimit.c
@@ -19,9 +19,9 @@
#include <unistd.h>
+#include "macro.h"
#include "ratelimit.h"
#include "time-util.h"
-#include "macro.h"
static void test_ratelimit_test(void) {
int i;
diff --git a/src/test/test-rbtree.c b/src/test/test-rbtree.c
new file mode 100644
index 0000000000..8ae416c557
--- /dev/null
+++ b/src/test/test-rbtree.c
@@ -0,0 +1,362 @@
+/***
+ This file is part of systemd. See COPYING for details.
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General 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/>.
+***/
+
+/*
+ * Tests for RB-Tree
+ */
+
+#undef NDEBUG
+#include <assert.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include "c-rbtree.h"
+
+/* verify that all API calls are exported */
+static void test_api(void) {
+ CRBTree t = {};
+ CRBNode n = C_RBNODE_INIT(n);
+
+ assert(!c_rbnode_is_linked(&n));
+
+ /* init, is_linked, add, remove, remove_init */
+
+ c_rbtree_add(&t, NULL, &t.root, &n);
+ assert(c_rbnode_is_linked(&n));
+
+ c_rbtree_remove_init(&t, &n);
+ assert(!c_rbnode_is_linked(&n));
+
+ c_rbtree_add(&t, NULL, &t.root, &n);
+ assert(c_rbnode_is_linked(&n));
+
+ c_rbtree_remove(&t, &n);
+ assert(c_rbnode_is_linked(&n)); /* @n wasn't touched */
+
+ c_rbnode_init(&n);
+ assert(!c_rbnode_is_linked(&n));
+
+ /* first, last, leftmost, rightmost, next, prev */
+
+ assert(!c_rbtree_first(&t));
+ assert(!c_rbtree_last(&t));
+ assert(&n == c_rbnode_leftmost(&n));
+ assert(&n == c_rbnode_rightmost(&n));
+ assert(!c_rbnode_next(&n));
+ assert(!c_rbnode_prev(&n));
+}
+
+/* copied from c-rbtree.c, relies on internal representation */
+static inline _Bool c_rbnode_is_red(CRBNode *n) {
+ return !((unsigned long)n->__parent_and_color & 1UL);
+}
+
+/* copied from c-rbtree.c, relies on internal representation */
+static inline _Bool c_rbnode_is_black(CRBNode *n) {
+ return !!((unsigned long)n->__parent_and_color & 1UL);
+}
+
+static size_t validate(CRBTree *t) {
+ unsigned int i_black, n_black;
+ CRBNode *n, *p, *o;
+ size_t count = 0;
+
+ assert(t);
+ assert(!t->root || c_rbnode_is_black(t->root));
+
+ /* traverse to left-most child, count black nodes */
+ i_black = 0;
+ n = t->root;
+ while (n && n->left) {
+ if (c_rbnode_is_black(n))
+ ++i_black;
+ n = n->left;
+ }
+ n_black = i_black;
+
+ /*
+ * Traverse tree and verify correctness:
+ * 1) A node is either red or black
+ * 2) The root is black
+ * 3) All leaves are black
+ * 4) Every red node must have two black child nodes
+ * 5) Every path to a leaf contains the same number of black nodes
+ *
+ * Note that NULL nodes are considered black, which is why we don't
+ * check for 3).
+ */
+ o = NULL;
+ while (n) {
+ ++count;
+
+ /* verify natural order */
+ assert(n > o);
+ o = n;
+
+ /* verify consistency */
+ assert(!n->right || c_rbnode_parent(n->right) == n);
+ assert(!n->left || c_rbnode_parent(n->left) == n);
+
+ /* verify 2) */
+ if (!c_rbnode_parent(n))
+ assert(c_rbnode_is_black(n));
+
+ if (c_rbnode_is_red(n)) {
+ /* verify 4) */
+ assert(!n->left || c_rbnode_is_black(n->left));
+ assert(!n->right || c_rbnode_is_black(n->right));
+ } else {
+ /* verify 1) */
+ assert(c_rbnode_is_black(n));
+ }
+
+ /* verify 5) */
+ if (!n->left && !n->right)
+ assert(i_black == n_black);
+
+ /* get next node */
+ if (n->right) {
+ n = n->right;
+ if (c_rbnode_is_black(n))
+ ++i_black;
+
+ while (n->left) {
+ n = n->left;
+ if (c_rbnode_is_black(n))
+ ++i_black;
+ }
+ } else {
+ while ((p = c_rbnode_parent(n)) && n == p->right) {
+ n = p;
+ if (c_rbnode_is_black(p->right))
+ --i_black;
+ }
+
+ n = p;
+ if (p && c_rbnode_is_black(p->left))
+ --i_black;
+ }
+ }
+
+ return count;
+}
+
+static void insert(CRBTree *t, CRBNode *n) {
+ CRBNode **i, *p;
+
+ assert(t);
+ assert(n);
+ assert(!c_rbnode_is_linked(n));
+
+ i = &t->root;
+ p = NULL;
+ while (*i) {
+ p = *i;
+ if (n < *i) {
+ i = &(*i)->left;
+ } else {
+ assert(n > *i);
+ i = &(*i)->right;
+ }
+ }
+
+ c_rbtree_add(t, p, i, n);
+}
+
+static void shuffle(void **nodes, size_t n_memb) {
+ unsigned int i, j;
+ void *t;
+
+ for (i = 0; i < n_memb; ++i) {
+ j = rand() % n_memb;
+ t = nodes[j];
+ nodes[j] = nodes[i];
+ nodes[i] = t;
+ }
+}
+
+/* run some pseudo-random tests on the tree */
+static void test_shuffle(void) {
+ CRBNode *nodes[256];
+ CRBTree t = {};
+ unsigned int i, j;
+ size_t n;
+
+ /* allocate and initialize all nodes */
+ for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
+ nodes[i] = malloc(sizeof(*nodes[i]));
+ assert(nodes[i]);
+ c_rbnode_init(nodes[i]);
+ }
+
+ /* shuffle nodes and validate *empty* tree */
+ shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes));
+ n = validate(&t);
+ assert(n == 0);
+
+ /* add all nodes and validate after each insertion */
+ for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
+ insert(&t, nodes[i]);
+ n = validate(&t);
+ assert(n == i + 1);
+ }
+
+ /* shuffle nodes again */
+ shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes));
+
+ /* remove all nodes (in different order) and validate on each round */
+ for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
+ c_rbtree_remove(&t, nodes[i]);
+ n = validate(&t);
+ assert(n == sizeof(nodes) / sizeof(*nodes) - i - 1);
+ c_rbnode_init(nodes[i]);
+ }
+
+ /* shuffle nodes and validate *empty* tree again */
+ shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes));
+ n = validate(&t);
+ assert(n == 0);
+
+ /* add all nodes again */
+ for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
+ insert(&t, nodes[i]);
+ n = validate(&t);
+ assert(n == i + 1);
+ }
+
+ /* 4 times, remove half of the nodes and add them again */
+ for (j = 0; j < 4; ++j) {
+ /* shuffle nodes again */
+ shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes));
+
+ /* remove half of the nodes */
+ for (i = 0; i < sizeof(nodes) / sizeof(*nodes) / 2; ++i) {
+ c_rbtree_remove(&t, nodes[i]);
+ n = validate(&t);
+ assert(n == sizeof(nodes) / sizeof(*nodes) - i - 1);
+ c_rbnode_init(nodes[i]);
+ }
+
+ /* shuffle the removed half */
+ shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes) / 2);
+
+ /* add the removed half again */
+ for (i = 0; i < sizeof(nodes) / sizeof(*nodes) / 2; ++i) {
+ insert(&t, nodes[i]);
+ n = validate(&t);
+ assert(n == sizeof(nodes) / sizeof(*nodes) / 2 + i + 1);
+ }
+ }
+
+ /* shuffle nodes again */
+ shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes));
+
+ /* remove all */
+ for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
+ c_rbtree_remove(&t, nodes[i]);
+ n = validate(&t);
+ assert(n == sizeof(nodes) / sizeof(*nodes) - i - 1);
+ c_rbnode_init(nodes[i]);
+ }
+
+ /* free nodes again */
+ for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i)
+ free(nodes[i]);
+}
+
+typedef struct {
+ unsigned long key;
+ CRBNode rb;
+} Node;
+
+#define node_from_rb(_rb) ((Node *)((char *)(_rb) - offsetof(Node, rb)))
+
+static int compare(CRBTree *t, void *k, CRBNode *n) {
+ unsigned long key = (unsigned long)k;
+ Node *node = node_from_rb(n);
+
+ return (key < node->key) ? -1 : (key > node->key) ? 1 : 0;
+}
+
+/* run tests against the c_rbtree_find*() helpers */
+static void test_map(void) {
+ CRBNode **slot, *p;
+ CRBTree t = {};
+ Node *nodes[2048];
+ unsigned long i;
+
+ /* allocate and initialize all nodes */
+ for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
+ nodes[i] = malloc(sizeof(*nodes[i]));
+ assert(nodes[i]);
+ nodes[i]->key = i;
+ c_rbnode_init(&nodes[i]->rb);
+ }
+
+ /* shuffle nodes */
+ shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes));
+
+ /* add all nodes, and verify that each node is linked */
+ for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
+ assert(!c_rbnode_is_linked(&nodes[i]->rb));
+ assert(!c_rbtree_find_entry(&t, compare, (void *)nodes[i]->key, Node, rb));
+
+ slot = c_rbtree_find_slot(&t, compare, (void *)nodes[i]->key, &p);
+ assert(slot);
+ c_rbtree_add(&t, p, slot, &nodes[i]->rb);
+
+ assert(c_rbnode_is_linked(&nodes[i]->rb));
+ assert(nodes[i] == c_rbtree_find_entry(&t, compare, (void *)nodes[i]->key, Node, rb));
+ }
+
+ /* shuffle nodes again */
+ shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes));
+
+ /* remove all nodes (in different order) */
+ for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
+ assert(c_rbnode_is_linked(&nodes[i]->rb));
+ assert(nodes[i] == c_rbtree_find_entry(&t, compare, (void *)nodes[i]->key, Node, rb));
+
+ c_rbtree_remove_init(&t, &nodes[i]->rb);
+
+ assert(!c_rbnode_is_linked(&nodes[i]->rb));
+ assert(!c_rbtree_find_entry(&t, compare, (void *)nodes[i]->key, Node, rb));
+ }
+
+ /* free nodes again */
+ for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i)
+ free(nodes[i]);
+}
+
+int main(int argc, char **argv) {
+ unsigned int i;
+
+ /* we want stable tests, so use fixed seed */
+ srand(0xdeadbeef);
+
+ test_api();
+
+ /*
+ * The tests are pseudo random; run them multiple times, each run will
+ * have different orders and thus different results.
+ */
+ for (i = 0; i < 4; ++i) {
+ test_shuffle();
+ test_map();
+ }
+
+ return 0;
+}
diff --git a/src/test/test-replace-var.c b/src/test/test-replace-var.c
index b1d42d77fd..2de2091561 100644
--- a/src/test/test-replace-var.c
+++ b/src/test/test-replace-var.c
@@ -21,9 +21,10 @@
#include <string.h>
-#include "util.h"
#include "macro.h"
#include "replace-var.h"
+#include "string-util.h"
+#include "util.h"
static char *lookup(const char *variable, void *userdata) {
return strjoin("<<<", variable, ">>>", NULL);
diff --git a/src/test/test-rlimit-util.c b/src/test/test-rlimit-util.c
new file mode 100644
index 0000000000..24bfe7a60e
--- /dev/null
+++ b/src/test/test-rlimit-util.c
@@ -0,0 +1,69 @@
+/***
+ 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.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General 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/resource.h>
+
+#include "capability-util.h"
+#include "macro.h"
+#include "rlimit-util.h"
+#include "string-util.h"
+#include "util.h"
+
+int main(int argc, char *argv[]) {
+ struct rlimit old, new, high;
+ struct rlimit err = {
+ .rlim_cur = 10,
+ .rlim_max = 5,
+ };
+
+ log_parse_environment();
+ log_open();
+
+ assert_se(drop_capability(CAP_SYS_RESOURCE) == 0);
+
+ assert_se(getrlimit(RLIMIT_NOFILE, &old) == 0);
+ new.rlim_cur = MIN(5U, old.rlim_max);
+ new.rlim_max = old.rlim_max;
+ assert_se(setrlimit(RLIMIT_NOFILE, &new) >= 0);
+
+ assert_se(rlimit_from_string("LimitNOFILE") == RLIMIT_NOFILE);
+ assert_se(rlimit_from_string("DefaultLimitNOFILE") == -1);
+
+ assert_se(streq_ptr(rlimit_to_string(RLIMIT_NOFILE), "LimitNOFILE"));
+ assert_se(rlimit_to_string(-1) == NULL);
+
+ assert_se(getrlimit(RLIMIT_NOFILE, &old) == 0);
+ assert_se(setrlimit_closest(RLIMIT_NOFILE, &old) == 0);
+ assert_se(getrlimit(RLIMIT_NOFILE, &new) == 0);
+ assert_se(old.rlim_cur == new.rlim_cur);
+ assert_se(old.rlim_max == new.rlim_max);
+
+ assert_se(getrlimit(RLIMIT_NOFILE, &old) == 0);
+ high = RLIMIT_MAKE_CONST(old.rlim_max == RLIM_INFINITY ? old.rlim_max : old.rlim_max + 1);
+ assert_se(setrlimit_closest(RLIMIT_NOFILE, &high) == 0);
+ assert_se(getrlimit(RLIMIT_NOFILE, &new) == 0);
+ assert_se(new.rlim_max == old.rlim_max);
+ assert_se(new.rlim_cur == new.rlim_max);
+
+ assert_se(getrlimit(RLIMIT_NOFILE, &old) == 0);
+ assert_se(setrlimit_closest(RLIMIT_NOFILE, &err) == -EINVAL);
+ assert_se(getrlimit(RLIMIT_NOFILE, &new) == 0);
+ assert_se(old.rlim_cur == new.rlim_cur);
+ assert_se(old.rlim_max == new.rlim_max);
+
+ return 0;
+}
diff --git a/src/test/test-sched-prio.c b/src/test/test-sched-prio.c
index ebc9110c4d..60b5160cec 100644
--- a/src/test/test-sched-prio.c
+++ b/src/test/test-sched-prio.c
@@ -21,8 +21,9 @@
#include <sched.h>
-#include "manager.h"
#include "macro.h"
+#include "manager.h"
+#include "test-helper.h"
int main(int argc, char *argv[]) {
Manager *m = NULL;
@@ -35,8 +36,8 @@ int main(int argc, char *argv[]) {
/* prepare the test */
assert_se(set_unit_path(TEST_DIR) >= 0);
r = manager_new(MANAGER_USER, true, &m);
- if (IN_SET(r, -EPERM, -EACCES, -EADDRINUSE, -EHOSTDOWN, -ENOENT, -ENOEXEC)) {
- printf("Skipping test: manager_new: %s", strerror(-r));
+ if (MANAGER_SKIP_TEST(r)) {
+ printf("Skipping test: manager_new: %s\n", strerror(-r));
return EXIT_TEST_SKIP;
}
assert_se(r >= 0);
diff --git a/src/test/test-sigbus.c b/src/test/test-sigbus.c
index f5bae65bef..b3ccc7509d 100644
--- a/src/test/test-sigbus.c
+++ b/src/test/test-sigbus.c
@@ -21,8 +21,9 @@
#include <sys/mman.h>
-#include "util.h"
+#include "fd-util.h"
#include "sigbus.h"
+#include "util.h"
int main(int argc, char *argv[]) {
_cleanup_close_ int fd = -1;
diff --git a/src/test/test-siphash24.c b/src/test/test-siphash24.c
new file mode 100644
index 0000000000..c20be99350
--- /dev/null
+++ b/src/test/test-siphash24.c
@@ -0,0 +1,85 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 Tom Gundersen
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General 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 "siphash24.h"
+#include "util.h"
+
+#define ITERATIONS 10000000ULL
+
+static int do_test(const uint8_t *in, size_t len, const uint8_t *key) {
+ struct siphash state = {};
+ uint64_t out = 0;
+ unsigned i, j;
+
+ out = siphash24(in, len, key);
+ assert_se(out == 0xa129ca6149be45e5);
+
+ /* verify the internal state as given in the above paper */
+ siphash24_init(&state, key);
+ assert_se(state.v0 == 0x7469686173716475);
+ assert_se(state.v1 == 0x6b617f6d656e6665);
+ assert_se(state.v2 == 0x6b7f62616d677361);
+ assert_se(state.v3 == 0x7b6b696e727e6c7b);
+ siphash24_compress(in, len, &state);
+ assert_se(state.v0 == 0x4a017198de0a59e0);
+ assert_se(state.v1 == 0x0d52f6f62a4f59a4);
+ assert_se(state.v2 == 0x634cb3577b01fd3d);
+ assert_se(state.v3 == 0xa5224d6f55c7d9c8);
+ out = siphash24_finalize(&state);
+ assert_se(out == 0xa129ca6149be45e5);
+ assert_se(state.v0 == 0xf6bcd53893fecff1);
+ assert_se(state.v1 == 0x54b9964c7ea0d937);
+ assert_se(state.v2 == 0x1b38329c099bb55a);
+ assert_se(state.v3 == 0x1814bb89ad7be679);
+
+ /* verify that decomposing the input in three chunks gives the
+ same result */
+ for (i = 0; i < len; i++) {
+ for (j = i; j < len; j++) {
+ siphash24_init(&state, key);
+ siphash24_compress(in, i, &state);
+ siphash24_compress(&in[i], j - i, &state);
+ siphash24_compress(&in[j], len - j, &state);
+ out = siphash24_finalize(&state);
+ assert_se(out == 0xa129ca6149be45e5);
+ }
+ }
+ return 0;
+}
+
+/* see https://131002.net/siphash/siphash.pdf, Appendix A */
+int main(int argc, char *argv[]) {
+ const uint8_t in[15] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e };
+ const uint8_t key[16] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
+ uint8_t in_buf[20];
+
+ /* Test with same input but different alignments. */
+ memcpy(in_buf, in, sizeof(in));
+ do_test(in_buf, sizeof(in), key);
+ memcpy(in_buf + 1, in, sizeof(in));
+ do_test(in_buf + 1, sizeof(in), key);
+ memcpy(in_buf + 2, in, sizeof(in));
+ do_test(in_buf + 2, sizeof(in), key);
+ memcpy(in_buf + 4, in, sizeof(in));
+ do_test(in_buf + 4, sizeof(in), key);
+}
diff --git a/src/test/test-sleep.c b/src/test/test-sleep.c
index 4308ddfb64..fb115ce4f3 100644
--- a/src/test/test-sleep.c
+++ b/src/test/test-sleep.c
@@ -21,10 +21,10 @@
#include <stdio.h>
-#include "util.h"
#include "log.h"
#include "sleep-config.h"
#include "strv.h"
+#include "util.h"
static void test_sleep(void) {
_cleanup_strv_free_ char
diff --git a/src/test/test-socket-util.c b/src/test/test-socket-util.c
index 2c18090ae5..33ff3755bc 100644
--- a/src/test/test-socket-util.c
+++ b/src/test/test-socket-util.c
@@ -17,12 +17,15 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "socket-util.h"
+#include "alloc-util.h"
+#include "async.h"
+#include "fd-util.h"
#include "in-addr-util.h"
-#include "util.h"
-#include "macro.h"
#include "log.h"
-#include "async.h"
+#include "macro.h"
+#include "socket-util.h"
+#include "string-util.h"
+#include "util.h"
static void test_socket_address_parse(void) {
SocketAddress a;
@@ -38,28 +41,25 @@ static void test_socket_address_parse(void) {
assert_se(socket_address_parse(&a, "65535") >= 0);
- if (socket_ipv6_is_supported()) {
- assert_se(socket_address_parse(&a, "[::1]") < 0);
- assert_se(socket_address_parse(&a, "[::1]8888") < 0);
- assert_se(socket_address_parse(&a, "::1") < 0);
- assert_se(socket_address_parse(&a, "[::1]:0") < 0);
- assert_se(socket_address_parse(&a, "[::1]:65536") < 0);
- assert_se(socket_address_parse(&a, "[a:b:1]:8888") < 0);
+ /* The checks below will pass even if ipv6 is disabled in
+ * kernel. The underlying glibc's inet_pton() is just a string
+ * parser and doesn't make any syscalls. */
- assert_se(socket_address_parse(&a, "8888") >= 0);
- assert_se(a.sockaddr.sa.sa_family == AF_INET6);
+ assert_se(socket_address_parse(&a, "[::1]") < 0);
+ assert_se(socket_address_parse(&a, "[::1]8888") < 0);
+ assert_se(socket_address_parse(&a, "::1") < 0);
+ assert_se(socket_address_parse(&a, "[::1]:0") < 0);
+ assert_se(socket_address_parse(&a, "[::1]:65536") < 0);
+ assert_se(socket_address_parse(&a, "[a:b:1]:8888") < 0);
- assert_se(socket_address_parse(&a, "[2001:0db8:0000:85a3:0000:0000:ac1f:8001]:8888") >= 0);
- assert_se(a.sockaddr.sa.sa_family == AF_INET6);
+ assert_se(socket_address_parse(&a, "8888") >= 0);
+ assert_se(a.sockaddr.sa.sa_family == (socket_ipv6_is_supported() ? AF_INET6 : AF_INET));
- assert_se(socket_address_parse(&a, "[::1]:8888") >= 0);
- assert_se(a.sockaddr.sa.sa_family == AF_INET6);
- } else {
- assert_se(socket_address_parse(&a, "[::1]:8888") < 0);
+ assert_se(socket_address_parse(&a, "[2001:0db8:0000:85a3:0000:0000:ac1f:8001]:8888") >= 0);
+ assert_se(a.sockaddr.sa.sa_family == AF_INET6);
- assert_se(socket_address_parse(&a, "8888") >= 0);
- assert_se(a.sockaddr.sa.sa_family == AF_INET);
- }
+ assert_se(socket_address_parse(&a, "[::1]:8888") >= 0);
+ assert_se(a.sockaddr.sa.sa_family == AF_INET6);
assert_se(socket_address_parse(&a, "192.168.1.254:8888") >= 0);
assert_se(a.sockaddr.sa.sa_family == AF_INET);
diff --git a/src/test/test-strbuf.c b/src/test/test-strbuf.c
index 4ec648ae66..1d8eda0c15 100644
--- a/src/test/test-strbuf.c
+++ b/src/test/test-strbuf.c
@@ -23,6 +23,7 @@
#include <string.h>
#include "strbuf.h"
+#include "string-util.h"
#include "strv.h"
#include "util.h"
diff --git a/src/test/test-string-util.c b/src/test/test-string-util.c
new file mode 100644
index 0000000000..12889ce873
--- /dev/null
+++ b/src/test/test-string-util.c
@@ -0,0 +1,107 @@
+/*-*- 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 "string-util.h"
+
+static void test_string_erase(void) {
+ char *x;
+
+ x = strdupa("");
+ assert_se(streq(string_erase(x), ""));
+
+ x = strdupa("1");
+ assert_se(streq(string_erase(x), "x"));
+
+ x = strdupa("12");
+ assert_se(streq(string_erase(x), "xx"));
+
+ x = strdupa("123");
+ assert_se(streq(string_erase(x), "xxx"));
+
+ x = strdupa("1234");
+ assert_se(streq(string_erase(x), "xxxx"));
+
+ x = strdupa("12345");
+ assert_se(streq(string_erase(x), "xxxxx"));
+
+ x = strdupa("123456");
+ assert_se(streq(string_erase(x), "xxxxxx"));
+
+ x = strdupa("1234567");
+ assert_se(streq(string_erase(x), "xxxxxxx"));
+
+ x = strdupa("12345678");
+ assert_se(streq(string_erase(x), "xxxxxxxx"));
+
+ x = strdupa("123456789");
+ assert_se(streq(string_erase(x), "xxxxxxxxx"));
+}
+
+static void test_ascii_strcasecmp_n(void) {
+
+ assert_se(ascii_strcasecmp_n("", "", 0) == 0);
+ assert_se(ascii_strcasecmp_n("", "", 1) == 0);
+ assert_se(ascii_strcasecmp_n("", "a", 1) < 0);
+ assert_se(ascii_strcasecmp_n("", "a", 2) < 0);
+ assert_se(ascii_strcasecmp_n("a", "", 1) > 0);
+ assert_se(ascii_strcasecmp_n("a", "", 2) > 0);
+ assert_se(ascii_strcasecmp_n("a", "a", 1) == 0);
+ assert_se(ascii_strcasecmp_n("a", "a", 2) == 0);
+ assert_se(ascii_strcasecmp_n("a", "b", 1) < 0);
+ assert_se(ascii_strcasecmp_n("a", "b", 2) < 0);
+ assert_se(ascii_strcasecmp_n("b", "a", 1) > 0);
+ assert_se(ascii_strcasecmp_n("b", "a", 2) > 0);
+ assert_se(ascii_strcasecmp_n("xxxxyxxxx", "xxxxYxxxx", 9) == 0);
+ assert_se(ascii_strcasecmp_n("xxxxxxxxx", "xxxxyxxxx", 9) < 0);
+ assert_se(ascii_strcasecmp_n("xxxxXxxxx", "xxxxyxxxx", 9) < 0);
+ assert_se(ascii_strcasecmp_n("xxxxxxxxx", "xxxxYxxxx", 9) < 0);
+ assert_se(ascii_strcasecmp_n("xxxxXxxxx", "xxxxYxxxx", 9) < 0);
+
+ assert_se(ascii_strcasecmp_n("xxxxYxxxx", "xxxxYxxxx", 9) == 0);
+ assert_se(ascii_strcasecmp_n("xxxxyxxxx", "xxxxxxxxx", 9) > 0);
+ assert_se(ascii_strcasecmp_n("xxxxyxxxx", "xxxxXxxxx", 9) > 0);
+ assert_se(ascii_strcasecmp_n("xxxxYxxxx", "xxxxxxxxx", 9) > 0);
+ assert_se(ascii_strcasecmp_n("xxxxYxxxx", "xxxxXxxxx", 9) > 0);
+}
+
+static void test_ascii_strcasecmp_nn(void) {
+ assert_se(ascii_strcasecmp_nn("", 0, "", 0) == 0);
+ assert_se(ascii_strcasecmp_nn("", 0, "", 1) < 0);
+ assert_se(ascii_strcasecmp_nn("", 1, "", 0) > 0);
+ assert_se(ascii_strcasecmp_nn("", 1, "", 1) == 0);
+
+ assert_se(ascii_strcasecmp_nn("aaaa", 4, "aaAa", 4) == 0);
+ assert_se(ascii_strcasecmp_nn("aaa", 3, "aaAa", 4) < 0);
+ assert_se(ascii_strcasecmp_nn("aaa", 4, "aaAa", 4) < 0);
+ assert_se(ascii_strcasecmp_nn("aaaa", 4, "aaA", 3) > 0);
+ assert_se(ascii_strcasecmp_nn("aaaa", 4, "AAA", 4) > 0);
+
+ assert_se(ascii_strcasecmp_nn("aaaa", 4, "bbbb", 4) < 0);
+ assert_se(ascii_strcasecmp_nn("aaAA", 4, "BBbb", 4) < 0);
+ assert_se(ascii_strcasecmp_nn("BBbb", 4, "aaaa", 4) > 0);
+}
+
+int main(int argc, char *argv[]) {
+ test_string_erase();
+ test_ascii_strcasecmp_n();
+ test_ascii_strcasecmp_nn();
+ return 0;
+}
diff --git a/src/test/test-strip-tab-ansi.c b/src/test/test-strip-tab-ansi.c
index 6cec8768b1..10fc98ced5 100644
--- a/src/test/test-strip-tab-ansi.c
+++ b/src/test/test-strip-tab-ansi.c
@@ -21,8 +21,9 @@
#include <stdio.h>
-#include "util.h"
+#include "string-util.h"
#include "terminal-util.h"
+#include "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 bff43950a9..c27f15283e 100644
--- a/src/test/test-strv.c
+++ b/src/test/test-strv.c
@@ -22,9 +22,11 @@
#include <string.h>
-#include "util.h"
+#include "alloc-util.h"
#include "specifier.h"
+#include "string-util.h"
#include "strv.h"
+#include "util.h"
static void test_specifier_printf(void) {
static const Specifier table[] = {
@@ -155,7 +157,7 @@ static void test_strv_join(void) {
static void test_strv_quote_unquote(const char* const *split, const char *quoted) {
_cleanup_free_ char *p;
- _cleanup_strv_free_ char **s;
+ _cleanup_strv_free_ char **s = NULL;
char **t;
int r;
@@ -166,7 +168,7 @@ static void test_strv_quote_unquote(const char* const *split, const char *quoted
assert_se(streq(p, quoted));
r = strv_split_extract(&s, quoted, WHITESPACE, EXTRACT_QUOTES);
- assert_se(r == 0);
+ assert_se(r == (int) strv_length(s));
assert_se(s);
STRV_FOREACH(t, s) {
assert_se(*t);
@@ -183,7 +185,7 @@ static void test_strv_unquote(const char *quoted, char **list) {
int r;
r = strv_split_extract(&s, quoted, WHITESPACE, EXTRACT_QUOTES);
- assert_se(r == 0);
+ assert_se(r == (int) strv_length(list));
assert_se(s);
j = strv_join(s, " | ");
assert_se(j);
@@ -225,7 +227,7 @@ static void test_strv_split_extract(void) {
int r;
r = strv_split_extract(&l, str, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
- assert_se(r == 0);
+ assert_se(r == (int) strv_length(l));
assert_se(streq_ptr(l[0], ""));
assert_se(streq_ptr(l[1], "foo:bar"));
assert_se(streq_ptr(l[2], ""));
@@ -341,11 +343,11 @@ static void test_strv_extend_strv(void) {
_cleanup_strv_free_ char **a = NULL, **b = NULL;
a = strv_new("abc", "def", "ghi", NULL);
- b = strv_new("jkl", "mno", "pqr", NULL);
+ b = strv_new("jkl", "mno", "abc", "pqr", NULL);
assert_se(a);
assert_se(b);
- assert_se(strv_extend_strv(&a, b) >= 0);
+ assert_se(strv_extend_strv(&a, b, true) == 3);
assert_se(streq(a[0], "abc"));
assert_se(streq(a[1], "def"));
@@ -569,6 +571,77 @@ static void test_strv_shell_escape(void) {
assert_se(streq_ptr(v[3], NULL));
}
+static void test_strv_skip_one(char **a, size_t n, char **b) {
+ a = strv_skip(a, n);
+ assert_se(strv_equal(a, b));
+}
+
+static void test_strv_skip(void) {
+ test_strv_skip_one(STRV_MAKE("foo", "bar", "baz"), 0, STRV_MAKE("foo", "bar", "baz"));
+ test_strv_skip_one(STRV_MAKE("foo", "bar", "baz"), 1, STRV_MAKE("bar", "baz"));
+ test_strv_skip_one(STRV_MAKE("foo", "bar", "baz"), 2, STRV_MAKE("baz"));
+ test_strv_skip_one(STRV_MAKE("foo", "bar", "baz"), 3, STRV_MAKE(NULL));
+ test_strv_skip_one(STRV_MAKE("foo", "bar", "baz"), 4, STRV_MAKE(NULL));
+ test_strv_skip_one(STRV_MAKE("foo", "bar", "baz"), 55, STRV_MAKE(NULL));
+
+ test_strv_skip_one(STRV_MAKE("quux"), 0, STRV_MAKE("quux"));
+ test_strv_skip_one(STRV_MAKE("quux"), 1, STRV_MAKE(NULL));
+ test_strv_skip_one(STRV_MAKE("quux"), 55, STRV_MAKE(NULL));
+
+ test_strv_skip_one(STRV_MAKE(NULL), 0, STRV_MAKE(NULL));
+ test_strv_skip_one(STRV_MAKE(NULL), 1, STRV_MAKE(NULL));
+ test_strv_skip_one(STRV_MAKE(NULL), 55, STRV_MAKE(NULL));
+}
+
+static void test_strv_extend_n(void) {
+ _cleanup_strv_free_ char **v = NULL;
+
+ v = strv_new("foo", "bar", NULL);
+ assert_se(v);
+
+ assert_se(strv_extend_n(&v, "waldo", 3) >= 0);
+ assert_se(strv_extend_n(&v, "piep", 2) >= 0);
+
+ assert_se(streq(v[0], "foo"));
+ assert_se(streq(v[1], "bar"));
+ assert_se(streq(v[2], "waldo"));
+ assert_se(streq(v[3], "waldo"));
+ assert_se(streq(v[4], "waldo"));
+ assert_se(streq(v[5], "piep"));
+ assert_se(streq(v[6], "piep"));
+ assert_se(v[7] == NULL);
+
+ v = strv_free(v);
+
+ assert_se(strv_extend_n(&v, "foo", 1) >= 0);
+ assert_se(strv_extend_n(&v, "bar", 0) >= 0);
+
+ assert_se(streq(v[0], "foo"));
+ assert_se(v[1] == NULL);
+}
+
+static void test_strv_make_nulstr_one(char **l) {
+ _cleanup_free_ char *b = NULL, *c = NULL;
+ _cleanup_strv_free_ char **q = NULL;
+ size_t n, m;
+
+ assert_se(strv_make_nulstr(l, &b, &n) >= 0);
+ assert_se(q = strv_parse_nulstr(b, n));
+ assert_se(strv_equal(l, q));
+
+ assert_se(strv_make_nulstr(q, &c, &m) >= 0);
+ assert_se(m == n);
+ assert_se(memcmp(b, c, m) == 0);
+}
+
+static void test_strv_make_nulstr(void) {
+ test_strv_make_nulstr_one(NULL);
+ test_strv_make_nulstr_one(STRV_MAKE(NULL));
+ test_strv_make_nulstr_one(STRV_MAKE("foo"));
+ test_strv_make_nulstr_one(STRV_MAKE("foo", "bar"));
+ test_strv_make_nulstr_one(STRV_MAKE("foo", "bar", "quuux"));
+}
+
int main(int argc, char *argv[]) {
test_specifier_printf();
test_strv_foreach();
@@ -627,6 +700,9 @@ int main(int argc, char *argv[]) {
test_strv_is_uniq();
test_strv_reverse();
test_strv_shell_escape();
+ test_strv_skip();
+ test_strv_extend_n();
+ test_strv_make_nulstr();
return 0;
}
diff --git a/src/test/test-strxcpyx.c b/src/test/test-strxcpyx.c
index 858a4081da..e411d479ab 100644
--- a/src/test/test-strxcpyx.c
+++ b/src/test/test-strxcpyx.c
@@ -21,8 +21,9 @@
#include <string.h>
-#include "util.h"
+#include "string-util.h"
#include "strxcpyx.h"
+#include "util.h"
static void test_strpcpy(void) {
char target[25];
diff --git a/src/test/test-tables.c b/src/test/test-tables.c
index 0e5ab1645f..aef992ee3c 100644
--- a/src/test/test-tables.c
+++ b/src/test/test-tables.c
@@ -17,7 +17,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "architecture.h"
#include "automount.h"
+#include "bus-xml-policy.h"
+#include "busname.h"
#include "cgroup.h"
#include "compress.h"
#include "condition.h"
@@ -25,31 +28,27 @@
#include "execute.h"
#include "install.h"
#include "job.h"
+#include "journald-server.h"
#include "kill.h"
+#include "link-config.h"
+#include "locale-util.h"
#include "log.h"
#include "logs-show.h"
#include "mount.h"
#include "path.h"
+#include "rlimit-util.h"
#include "scope.h"
#include "service.h"
#include "slice.h"
-#include "snapshot.h"
#include "socket-util.h"
#include "socket.h"
#include "swap.h"
#include "target.h"
+#include "test-tables.h"
#include "timer.h"
#include "unit-name.h"
#include "unit.h"
#include "util.h"
-#include "architecture.h"
-#include "link-config.h"
-#include "bus-xml-policy.h"
-#include "busname.h"
-#include "journald-server.h"
-#include "locale-util.h"
-
-#include "test-tables.h"
int main(int argc, char **argv) {
test_table(architecture, ARCHITECTURE);
@@ -97,7 +96,6 @@ int main(int argc, char **argv) {
test_table(service_state, SERVICE_STATE);
test_table(service_type, SERVICE_TYPE);
test_table(slice_state, SLICE_STATE);
- test_table(snapshot_state, SNAPSHOT_STATE);
test_table(socket_address_bind_ipv6_only, SOCKET_ADDRESS_BIND_IPV6_ONLY);
test_table(socket_exec_command, SOCKET_EXEC_COMMAND);
test_table(socket_result, SOCKET_RESULT);
diff --git a/src/test/test-terminal-util.c b/src/test/test-terminal-util.c
index d81fdb9923..84b448a095 100644
--- a/src/test/test-terminal-util.c
+++ b/src/test/test-terminal-util.c
@@ -18,13 +18,15 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
#include <stdbool.h>
+#include <stdio.h>
-#include "terminal-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "log.h"
#include "macro.h"
+#include "terminal-util.h"
#include "util.h"
-#include "log.h"
static void test_default_term_for_tty(void) {
puts(default_term_for_tty("/dev/tty23"));
diff --git a/src/test/test-time.c b/src/test/test-time.c
index 3840fff061..8896b2c92b 100644
--- a/src/test/test-time.c
+++ b/src/test/test-time.c
@@ -19,8 +19,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "time-util.h"
#include "strv.h"
+#include "time-util.h"
static void test_parse_sec(void) {
usec_t u;
@@ -57,6 +57,28 @@ static void test_parse_sec(void) {
assert_se(parse_sec(".3 infinity", &u) < 0);
}
+static void test_parse_time(void) {
+ usec_t u;
+
+ assert_se(parse_time("5", &u, 1) >= 0);
+ assert_se(u == 5);
+
+ assert_se(parse_time("5", &u, USEC_PER_MSEC) >= 0);
+ assert_se(u == 5 * USEC_PER_MSEC);
+
+ assert_se(parse_time("5", &u, USEC_PER_SEC) >= 0);
+ assert_se(u == 5 * USEC_PER_SEC);
+
+ assert_se(parse_time("5s", &u, 1) >= 0);
+ assert_se(u == 5 * USEC_PER_SEC);
+
+ assert_se(parse_time("5s", &u, USEC_PER_SEC) >= 0);
+ assert_se(u == 5 * USEC_PER_SEC);
+
+ assert_se(parse_time("5s", &u, USEC_PER_MSEC) >= 0);
+ assert_se(u == 5 * USEC_PER_SEC);
+}
+
static void test_parse_nsec(void) {
nsec_t u;
@@ -161,6 +183,7 @@ static void test_get_timezones(void) {
int main(int argc, char *argv[]) {
test_parse_sec();
+ test_parse_time();
test_parse_nsec();
test_format_timespan(1);
test_format_timespan(USEC_PER_MSEC);
diff --git a/src/test/test-tmpfiles.c b/src/test/test-tmpfiles.c
index 221dd67eb2..23f26369bd 100644
--- a/src/test/test-tmpfiles.c
+++ b/src/test/test-tmpfiles.c
@@ -20,31 +20,46 @@
***/
#include <fcntl.h>
-#include <unistd.h>
-#include <stdlib.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
-#include "util.h"
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fileio.h"
#include "formats-util.h"
+#include "fs-util.h"
+#include "log.h"
+#include "string-util.h"
+#include "util.h"
int main(int argc, char** argv) {
const char *p = argv[1] ?: "/tmp";
char *pattern = strjoina(p, "/systemd-test-XXXXXX");
_cleanup_close_ int fd, fd2;
- _cleanup_free_ char *cmd, *cmd2;
+ _cleanup_free_ char *cmd, *cmd2, *ans, *ans2;
+
+ log_set_max_level(LOG_DEBUG);
+ log_parse_environment();
fd = open_tmpfile(p, O_RDWR|O_CLOEXEC);
assert_se(fd >= 0);
assert_se(asprintf(&cmd, "ls -l /proc/"PID_FMT"/fd/%d", getpid(), fd) > 0);
- system(cmd);
+ (void) system(cmd);
+ assert_se(readlink_malloc(cmd + 6, &ans) >= 0);
+ log_debug("link1: %s", ans);
+ assert_se(endswith(ans, " (deleted)"));
fd2 = mkostemp_safe(pattern, O_RDWR|O_CLOEXEC);
assert_se(fd >= 0);
assert_se(unlink(pattern) == 0);
assert_se(asprintf(&cmd2, "ls -l /proc/"PID_FMT"/fd/%d", getpid(), fd2) > 0);
- system(cmd2);
+ (void) system(cmd2);
+ assert_se(readlink_malloc(cmd2 + 6, &ans2) >= 0);
+ log_debug("link2: %s", ans2);
+ assert_se(endswith(ans2, " (deleted)"));
return 0;
}
diff --git a/src/test/test-udev.c b/src/test/test-udev.c
index 2b765a3e90..9cc64f7c68 100644
--- a/src/test/test-udev.c
+++ b/src/test/test-udev.c
@@ -18,19 +18,21 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
-#include <stdlib.h>
#include <errno.h>
-#include <unistd.h>
#include <sched.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <sys/mount.h>
#include <sys/signalfd.h>
+#include <unistd.h>
+#include "fs-util.h"
#include "missing.h"
#include "selinux-util.h"
#include "signal-util.h"
-#include "udev.h"
+#include "string-util.h"
#include "udev-util.h"
+#include "udev.h"
static int fake_filesystems(void) {
static const struct fakefs {
@@ -42,7 +44,7 @@ static int fake_filesystems(void) {
{ "test/dev", "/dev", "failed to mount test /dev" },
{ "test/run", "/run", "failed to mount test /run" },
{ "test/run", "/etc/udev/rules.d", "failed to mount empty /etc/udev/rules.d" },
- { "test/run", "/usr/lib/udev/rules.d", "failed to mount empty /usr/lib/udev/rules.d" },
+ { "test/run", UDEVLIBEXECDIR "/rules.d","failed to mount empty " UDEVLIBEXECDIR "/rules.d" },
};
unsigned int i;
int err;
@@ -64,7 +66,7 @@ static int fake_filesystems(void) {
err = mount(fakefss[i].src, fakefss[i].target, NULL, MS_BIND, NULL);
if (err < 0) {
err = -errno;
- fprintf(stderr, "%s %m", fakefss[i].error);
+ fprintf(stderr, "%s %m\n", fakefss[i].error);
return err;
}
}
diff --git a/src/test/test-uid-range.c b/src/test/test-uid-range.c
index bc5baa2fcb..4dcf10e26d 100644
--- a/src/test/test-uid-range.c
+++ b/src/test/test-uid-range.c
@@ -21,8 +21,10 @@
#include <stddef.h>
-#include "util.h"
+#include "alloc-util.h"
#include "uid-range.h"
+#include "user-util.h"
+#include "util.h"
int main(int argc, char *argv[]) {
_cleanup_free_ UidRange *p = NULL;
diff --git a/src/test/test-unaligned.c b/src/test/test-unaligned.c
index 1754d06b2d..b18b3fca0e 100644
--- a/src/test/test-unaligned.c
+++ b/src/test/test-unaligned.c
@@ -17,8 +17,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "unaligned.h"
#include "sparse-endian.h"
+#include "unaligned.h"
#include "util.h"
static uint8_t data[] = {
@@ -26,7 +26,7 @@ static uint8_t data[] = {
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
};
-int main(int argc, const char *argv[]) {
+static void test_be(void) {
uint8_t scratch[16];
assert_se(unaligned_read_be16(&data[0]) == 0x0001);
@@ -91,3 +91,75 @@ int main(int argc, const char *argv[]) {
unaligned_write_be64(&scratch[7], 0x0708090a0b0c0d0e);
assert_se(memcmp(&scratch[7], &data[7], sizeof(uint64_t)) == 0);
}
+
+static void test_le(void) {
+ uint8_t scratch[16];
+
+ assert_se(unaligned_read_le16(&data[0]) == 0x0100);
+ assert_se(unaligned_read_le16(&data[1]) == 0x0201);
+
+ assert_se(unaligned_read_le32(&data[0]) == 0x03020100);
+ assert_se(unaligned_read_le32(&data[1]) == 0x04030201);
+ assert_se(unaligned_read_le32(&data[2]) == 0x05040302);
+ assert_se(unaligned_read_le32(&data[3]) == 0x06050403);
+
+ assert_se(unaligned_read_le64(&data[0]) == 0x0706050403020100);
+ assert_se(unaligned_read_le64(&data[1]) == 0x0807060504030201);
+ assert_se(unaligned_read_le64(&data[2]) == 0x0908070605040302);
+ assert_se(unaligned_read_le64(&data[3]) == 0x0a09080706050403);
+ assert_se(unaligned_read_le64(&data[4]) == 0x0b0a090807060504);
+ assert_se(unaligned_read_le64(&data[5]) == 0x0c0b0a0908070605);
+ assert_se(unaligned_read_le64(&data[6]) == 0x0d0c0b0a09080706);
+ assert_se(unaligned_read_le64(&data[7]) == 0x0e0d0c0b0a090807);
+
+ zero(scratch);
+ unaligned_write_le16(&scratch[0], 0x0100);
+ assert_se(memcmp(&scratch[0], &data[0], sizeof(uint16_t)) == 0);
+ zero(scratch);
+ unaligned_write_le16(&scratch[1], 0x0201);
+ assert_se(memcmp(&scratch[1], &data[1], sizeof(uint16_t)) == 0);
+
+ zero(scratch);
+ unaligned_write_le32(&scratch[0], 0x03020100);
+
+ assert_se(memcmp(&scratch[0], &data[0], sizeof(uint32_t)) == 0);
+ zero(scratch);
+ unaligned_write_le32(&scratch[1], 0x04030201);
+ assert_se(memcmp(&scratch[1], &data[1], sizeof(uint32_t)) == 0);
+ zero(scratch);
+ unaligned_write_le32(&scratch[2], 0x05040302);
+ assert_se(memcmp(&scratch[2], &data[2], sizeof(uint32_t)) == 0);
+ zero(scratch);
+ unaligned_write_le32(&scratch[3], 0x06050403);
+ assert_se(memcmp(&scratch[3], &data[3], sizeof(uint32_t)) == 0);
+
+ zero(scratch);
+ unaligned_write_le64(&scratch[0], 0x0706050403020100);
+ assert_se(memcmp(&scratch[0], &data[0], sizeof(uint64_t)) == 0);
+ zero(scratch);
+ unaligned_write_le64(&scratch[1], 0x0807060504030201);
+ assert_se(memcmp(&scratch[1], &data[1], sizeof(uint64_t)) == 0);
+ zero(scratch);
+ unaligned_write_le64(&scratch[2], 0x0908070605040302);
+ assert_se(memcmp(&scratch[2], &data[2], sizeof(uint64_t)) == 0);
+ zero(scratch);
+ unaligned_write_le64(&scratch[3], 0x0a09080706050403);
+ assert_se(memcmp(&scratch[3], &data[3], sizeof(uint64_t)) == 0);
+ zero(scratch);
+ unaligned_write_le64(&scratch[4], 0x0B0A090807060504);
+ assert_se(memcmp(&scratch[4], &data[4], sizeof(uint64_t)) == 0);
+ zero(scratch);
+ unaligned_write_le64(&scratch[5], 0x0c0b0a0908070605);
+ assert_se(memcmp(&scratch[5], &data[5], sizeof(uint64_t)) == 0);
+ zero(scratch);
+ unaligned_write_le64(&scratch[6], 0x0d0c0b0a09080706);
+ assert_se(memcmp(&scratch[6], &data[6], sizeof(uint64_t)) == 0);
+ zero(scratch);
+ unaligned_write_le64(&scratch[7], 0x0e0d0c0b0a090807);
+ assert_se(memcmp(&scratch[7], &data[7], sizeof(uint64_t)) == 0);
+}
+
+int main(int argc, const char *argv[]) {
+ test_be();
+ test_le();
+}
diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c
index 8358789e6f..cd1e4e4698 100644
--- a/src/test/test-unit-file.c
+++ b/src/test/test-unit-file.c
@@ -20,23 +20,29 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
+#include <fcntl.h>
#include <stddef.h>
+#include <stdio.h>
#include <string.h>
+#include <sys/capability.h>
#include <unistd.h>
-#include <fcntl.h>
-#include "install.h"
-#include "install-printf.h"
-#include "specifier.h"
-#include "util.h"
-#include "macro.h"
+#include "alloc-util.h"
+#include "capability-util.h"
+#include "fd-util.h"
+#include "fileio.h"
#include "hashmap.h"
+#include "hostname-util.h"
+#include "install-printf.h"
+#include "install.h"
#include "load-fragment.h"
+#include "macro.h"
+#include "specifier.h"
+#include "string-util.h"
#include "strv.h"
-#include "fileio.h"
#include "test-helper.h"
-#include "hostname-util.h"
+#include "user-util.h"
+#include "util.h"
static int test_unit_file_get_set(void) {
int r;
@@ -554,76 +560,266 @@ static void test_load_env_file_5(void) {
static void test_install_printf(void) {
char name[] = "name.service",
- path[] = "/run/systemd/system/name.service",
- user[] = "xxxx-no-such-user";
- UnitFileInstallInfo i = {name, path, user};
- UnitFileInstallInfo i2 = {name, path, NULL};
+ path[] = "/run/systemd/system/name.service";
+ UnitFileInstallInfo i = { .name = name, .path = path, };
+ UnitFileInstallInfo i2 = { .name= name, .path = path, };
char name3[] = "name@inst.service",
path3[] = "/run/systemd/system/name.service";
- UnitFileInstallInfo i3 = {name3, path3, user};
- UnitFileInstallInfo i4 = {name3, path3, NULL};
+ UnitFileInstallInfo i3 = { .name = name3, .path = path3, };
+ UnitFileInstallInfo i4 = { .name = name3, .path = path3, };
- _cleanup_free_ char *mid, *bid, *host;
+ _cleanup_free_ char *mid = NULL, *bid = NULL, *host = NULL, *uid = NULL, *user = NULL;
assert_se(specifier_machine_id('m', NULL, NULL, &mid) >= 0 && mid);
assert_se(specifier_boot_id('b', NULL, NULL, &bid) >= 0 && bid);
assert_se((host = gethostname_malloc()));
+ assert_se((user = getusername_malloc()));
+ assert_se(asprintf(&uid, UID_FMT, getuid()) >= 0);
#define expect(src, pattern, result) \
do { \
_cleanup_free_ char *t = NULL; \
_cleanup_free_ char \
*d1 = strdup(i.name), \
- *d2 = strdup(i.path), \
- *d3 = strdup(i.user); \
+ *d2 = strdup(i.path); \
assert_se(install_full_printf(&src, pattern, &t) >= 0 || !result); \
memzero(i.name, strlen(i.name)); \
memzero(i.path, strlen(i.path)); \
- memzero(i.user, strlen(i.user)); \
- assert_se(d1 && d2 && d3); \
+ assert_se(d1 && d2); \
if (result) { \
printf("%s\n", t); \
- assert_se(streq(t, result)); \
- } else assert_se(t == NULL); \
+ assert_se(streq(t, result)); \
+ } else assert_se(t == NULL); \
strcpy(i.name, d1); \
strcpy(i.path, d2); \
- strcpy(i.user, d3); \
} while(false)
- assert_se(setenv("USER", "root", 1) == 0);
-
expect(i, "%n", "name.service");
expect(i, "%N", "name");
expect(i, "%p", "name");
expect(i, "%i", "");
- expect(i, "%u", "xxxx-no-such-user");
-
- DISABLE_WARNING_NONNULL;
- expect(i, "%U", NULL);
- REENABLE_WARNING;
+ expect(i, "%u", user);
+ expect(i, "%U", uid);
expect(i, "%m", mid);
expect(i, "%b", bid);
expect(i, "%H", host);
- expect(i2, "%u", "root");
- expect(i2, "%U", "0");
+ expect(i2, "%u", user);
+ expect(i2, "%U", uid);
expect(i3, "%n", "name@inst.service");
expect(i3, "%N", "name@inst");
expect(i3, "%p", "name");
- expect(i3, "%u", "xxxx-no-such-user");
-
- DISABLE_WARNING_NONNULL;
- expect(i3, "%U", NULL);
- REENABLE_WARNING;
+ expect(i3, "%u", user);
+ expect(i3, "%U", uid);
expect(i3, "%m", mid);
expect(i3, "%b", bid);
expect(i3, "%H", host);
- expect(i4, "%u", "root");
- expect(i4, "%U", "0");
+ expect(i4, "%u", user);
+ expect(i4, "%U", uid);
+}
+
+static uint64_t make_cap(int cap) {
+ return ((uint64_t) 1ULL << (uint64_t) cap);
+}
+
+static void test_config_parse_capability_set(void) {
+ /* int config_parse_capability_set(
+ 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 r;
+ uint64_t capability_bounding_set = 0;
+
+ r = config_parse_capability_set(NULL, "fake", 1, "section", 1,
+ "CapabilityBoundingSet", 0, "CAP_NET_RAW",
+ &capability_bounding_set, NULL);
+ assert_se(r >= 0);
+ assert_se(capability_bounding_set == make_cap(CAP_NET_RAW));
+
+ r = config_parse_capability_set(NULL, "fake", 1, "section", 1,
+ "CapabilityBoundingSet", 0, "CAP_NET_ADMIN",
+ &capability_bounding_set, NULL);
+ assert_se(r >= 0);
+ assert_se(capability_bounding_set == (make_cap(CAP_NET_RAW) | make_cap(CAP_NET_ADMIN)));
+
+ r = config_parse_capability_set(NULL, "fake", 1, "section", 1,
+ "CapabilityBoundingSet", 0, "",
+ &capability_bounding_set, NULL);
+ assert_se(r >= 0);
+ assert_se(capability_bounding_set == UINT64_C(0));
+
+ r = config_parse_capability_set(NULL, "fake", 1, "section", 1,
+ "CapabilityBoundingSet", 0, "~",
+ &capability_bounding_set, NULL);
+ assert_se(r >= 0);
+ assert_se(cap_test_all(capability_bounding_set));
+
+ capability_bounding_set = 0;
+ r = config_parse_capability_set(NULL, "fake", 1, "section", 1,
+ "CapabilityBoundingSet", 0, " 'CAP_NET_RAW' WAT_CAP??? CAP_NET_ADMIN CAP'_trailing_garbage",
+ &capability_bounding_set, NULL);
+ assert_se(r >= 0);
+ assert_se(capability_bounding_set == (make_cap(CAP_NET_RAW) | make_cap(CAP_NET_ADMIN)));
+}
+
+static void test_config_parse_rlimit(void) {
+ struct rlimit * rl[_RLIMIT_MAX] = {};
+
+ assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "55", rl, NULL) >= 0);
+ assert_se(rl[RLIMIT_NOFILE]);
+ assert_se(rl[RLIMIT_NOFILE]->rlim_cur == 55);
+ assert_se(rl[RLIMIT_NOFILE]->rlim_cur == rl[RLIMIT_NOFILE]->rlim_max);
+
+ assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "55:66", rl, NULL) >= 0);
+ assert_se(rl[RLIMIT_NOFILE]);
+ assert_se(rl[RLIMIT_NOFILE]->rlim_cur == 55);
+ assert_se(rl[RLIMIT_NOFILE]->rlim_max == 66);
+
+ assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "infinity", rl, NULL) >= 0);
+ assert_se(rl[RLIMIT_NOFILE]);
+ assert_se(rl[RLIMIT_NOFILE]->rlim_cur == RLIM_INFINITY);
+ assert_se(rl[RLIMIT_NOFILE]->rlim_cur == rl[RLIMIT_NOFILE]->rlim_max);
+
+ assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "infinity:infinity", rl, NULL) >= 0);
+ assert_se(rl[RLIMIT_NOFILE]);
+ assert_se(rl[RLIMIT_NOFILE]->rlim_cur == RLIM_INFINITY);
+ assert_se(rl[RLIMIT_NOFILE]->rlim_cur == rl[RLIMIT_NOFILE]->rlim_max);
+
+ assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "10:20:30", rl, NULL) >= 0);
+ assert_se(rl[RLIMIT_NOFILE]);
+ assert_se(rl[RLIMIT_NOFILE]->rlim_cur == 10);
+ assert_se(rl[RLIMIT_NOFILE]->rlim_max == 20);
+
+ /* Invalid values don't change rl */
+ assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "wat:wat", rl, NULL) >= 0);
+ assert_se(rl[RLIMIT_NOFILE]);
+ assert_se(rl[RLIMIT_NOFILE]->rlim_cur == 10);
+ assert_se(rl[RLIMIT_NOFILE]->rlim_max == 20);
+
+ assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "66:wat", rl, NULL) >= 0);
+ assert_se(rl[RLIMIT_NOFILE]);
+ assert_se(rl[RLIMIT_NOFILE]->rlim_cur == 10);
+ assert_se(rl[RLIMIT_NOFILE]->rlim_max == 20);
+
+ assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "200:100", rl, NULL) >= 0);
+ assert_se(rl[RLIMIT_NOFILE]);
+ assert_se(rl[RLIMIT_NOFILE]->rlim_cur == 10);
+ assert_se(rl[RLIMIT_NOFILE]->rlim_max == 20);
+
+ rl[RLIMIT_NOFILE] = mfree(rl[RLIMIT_NOFILE]);
+
+ assert_se(config_parse_sec_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "56", rl, NULL) >= 0);
+ assert_se(rl[RLIMIT_CPU]);
+ assert_se(rl[RLIMIT_CPU]->rlim_cur == 56);
+ assert_se(rl[RLIMIT_CPU]->rlim_cur == rl[RLIMIT_CPU]->rlim_max);
+
+ assert_se(config_parse_sec_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "57s", rl, NULL) >= 0);
+ assert_se(rl[RLIMIT_CPU]);
+ assert_se(rl[RLIMIT_CPU]->rlim_cur == 57);
+ assert_se(rl[RLIMIT_CPU]->rlim_cur == rl[RLIMIT_CPU]->rlim_max);
+
+ assert_se(config_parse_sec_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "40s:1m", rl, NULL) >= 0);
+ assert_se(rl[RLIMIT_CPU]);
+ assert_se(rl[RLIMIT_CPU]->rlim_cur == 40);
+ assert_se(rl[RLIMIT_CPU]->rlim_max == 60);
+
+ assert_se(config_parse_sec_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "infinity", rl, NULL) >= 0);
+ assert_se(rl[RLIMIT_CPU]);
+ assert_se(rl[RLIMIT_CPU]->rlim_cur == RLIM_INFINITY);
+ assert_se(rl[RLIMIT_CPU]->rlim_cur == rl[RLIMIT_CPU]->rlim_max);
+
+ assert_se(config_parse_sec_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "1234ms", rl, NULL) >= 0);
+ assert_se(rl[RLIMIT_CPU]);
+ assert_se(rl[RLIMIT_CPU]->rlim_cur == 2);
+ assert_se(rl[RLIMIT_CPU]->rlim_cur == rl[RLIMIT_CPU]->rlim_max);
+
+ rl[RLIMIT_CPU] = mfree(rl[RLIMIT_CPU]);
+
+ assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "58", rl, NULL) >= 0);
+ assert_se(rl[RLIMIT_RTTIME]);
+ assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 58);
+ assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max);
+
+ assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "58:60", rl, NULL) >= 0);
+ assert_se(rl[RLIMIT_RTTIME]);
+ assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 58);
+ assert_se(rl[RLIMIT_RTTIME]->rlim_max == 60);
+
+ assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "59s", rl, NULL) >= 0);
+ assert_se(rl[RLIMIT_RTTIME]);
+ assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 59 * USEC_PER_SEC);
+ assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max);
+
+ assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "59s:123s", rl, NULL) >= 0);
+ assert_se(rl[RLIMIT_RTTIME]);
+ assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 59 * USEC_PER_SEC);
+ assert_se(rl[RLIMIT_RTTIME]->rlim_max == 123 * USEC_PER_SEC);
+
+ assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "infinity", rl, NULL) >= 0);
+ assert_se(rl[RLIMIT_RTTIME]);
+ assert_se(rl[RLIMIT_RTTIME]->rlim_cur == RLIM_INFINITY);
+ assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max);
+
+ assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "infinity:infinity", rl, NULL) >= 0);
+ assert_se(rl[RLIMIT_RTTIME]);
+ assert_se(rl[RLIMIT_RTTIME]->rlim_cur == RLIM_INFINITY);
+ assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max);
+
+ assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "2345ms", rl, NULL) >= 0);
+ assert_se(rl[RLIMIT_RTTIME]);
+ assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 2345 * USEC_PER_MSEC);
+ assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max);
+
+ rl[RLIMIT_RTTIME] = mfree(rl[RLIMIT_RTTIME]);
+}
+
+static void test_config_parse_pass_environ(void) {
+ /* int config_parse_pass_environ(
+ 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 r;
+ _cleanup_strv_free_ char **passenv = NULL;
+
+ r = config_parse_pass_environ(NULL, "fake", 1, "section", 1,
+ "PassEnvironment", 0, "A B",
+ &passenv, NULL);
+ assert_se(r >= 0);
+ assert_se(strv_length(passenv) == 2);
+ assert_se(streq(passenv[0], "A"));
+ assert_se(streq(passenv[1], "B"));
+
+ r = config_parse_pass_environ(NULL, "fake", 1, "section", 1,
+ "PassEnvironment", 0, "",
+ &passenv, NULL);
+ assert_se(r >= 0);
+ assert_se(strv_isempty(passenv));
+
+ r = config_parse_pass_environ(NULL, "fake", 1, "section", 1,
+ "PassEnvironment", 0, "'invalid name' 'normal_name' A=1 \\",
+ &passenv, NULL);
+ assert_se(r >= 0);
+ assert_se(strv_length(passenv) == 1);
+ assert_se(streq(passenv[0], "normal_name"));
+
}
int main(int argc, char *argv[]) {
@@ -634,6 +830,9 @@ int main(int argc, char *argv[]) {
r = test_unit_file_get_set();
test_config_parse_exec();
+ test_config_parse_capability_set();
+ test_config_parse_rlimit();
+ test_config_parse_pass_environ();
test_load_env_file_1();
test_load_env_file_2();
test_load_env_file_3();
diff --git a/src/test/test-unit-name.c b/src/test/test-unit-name.c
index e5405fb7f3..842ca40102 100644
--- a/src/test/test-unit-name.c
+++ b/src/test/test-unit-name.c
@@ -21,21 +21,24 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <pwd.h>
+#include "alloc-util.h"
+#include "hostname-util.h"
+#include "macro.h"
#include "manager.h"
-#include "unit.h"
+#include "path-util.h"
+#include "specifier.h"
+#include "string-util.h"
+#include "test-helper.h"
#include "unit-name.h"
#include "unit-printf.h"
-#include "specifier.h"
+#include "unit.h"
+#include "user-util.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));
@@ -191,15 +194,15 @@ static int test_unit_printf(void) {
Unit *u, *u2;
int r;
- _cleanup_free_ char *mid, *bid, *host, *root_uid;
- struct passwd *root;
+ _cleanup_free_ char *mid = NULL, *bid = NULL, *host = NULL, *uid = NULL, *user = NULL, *shell = NULL, *home = NULL;
assert_se(specifier_machine_id('m', NULL, NULL, &mid) >= 0 && mid);
assert_se(specifier_boot_id('b', NULL, NULL, &bid) >= 0 && bid);
- assert_se((host = gethostname_malloc()));
-
- assert_se((root = getpwnam("root")));
- assert_se(asprintf(&root_uid, "%d", (int) root->pw_uid) > 0);
+ assert_se(host = gethostname_malloc());
+ assert_se(user = getusername_malloc());
+ assert_se(asprintf(&uid, UID_FMT, getuid()));
+ assert_se(get_home_dir(&home) >= 0);
+ assert_se(get_shell(&shell) >= 0);
r = manager_new(MANAGER_USER, true, &m);
if (r == -EPERM || r == -EACCES || r == -EADDRINUSE) {
@@ -220,8 +223,6 @@ static int test_unit_printf(void) {
assert_se(streq(t, expected)); \
}
- assert_se(setenv("USER", "root", 1) == 0);
- assert_se(setenv("HOME", "/root", 1) == 0);
assert_se(setenv("XDG_RUNTIME_DIR", "/run/user/1/", 1) == 0);
assert_se(u = unit_new(m, sizeof(Service)));
@@ -240,9 +241,9 @@ static int test_unit_printf(void) {
expect(u, "%p", "blah");
expect(u, "%P", "blah");
expect(u, "%i", "");
- expect(u, "%u", root->pw_name);
- expect(u, "%U", root_uid);
- expect(u, "%h", root->pw_dir);
+ expect(u, "%u", user);
+ expect(u, "%U", uid);
+ expect(u, "%h", home);
expect(u, "%m", mid);
expect(u, "%b", bid);
expect(u, "%H", host);
@@ -260,9 +261,9 @@ static int test_unit_printf(void) {
expect(u2, "%P", "blah");
expect(u2, "%i", "foo-foo");
expect(u2, "%I", "foo/foo");
- expect(u2, "%u", root->pw_name);
- expect(u2, "%U", root_uid);
- expect(u2, "%h", root->pw_dir);
+ expect(u2, "%u", user);
+ expect(u2, "%U", uid);
+ expect(u2, "%h", home);
expect(u2, "%m", mid);
expect(u2, "%b", bid);
expect(u2, "%H", host);
diff --git a/src/test/test-user-util.c b/src/test/test-user-util.c
new file mode 100644
index 0000000000..09d37087e5
--- /dev/null
+++ b/src/test/test-user-util.c
@@ -0,0 +1,54 @@
+/*-*- 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 "alloc-util.h"
+#include "macro.h"
+#include "string-util.h"
+#include "user-util.h"
+#include "util.h"
+
+static void test_uid_to_name_one(uid_t uid, const char *name) {
+ _cleanup_free_ char *t = NULL;
+
+ assert_se(t = uid_to_name(uid));
+ assert_se(streq_ptr(t, name));
+}
+
+static void test_gid_to_name_one(gid_t gid, const char *name) {
+ _cleanup_free_ char *t = NULL;
+
+ assert_se(t = gid_to_name(gid));
+ assert_se(streq_ptr(t, name));
+}
+
+int main(int argc, char*argv[]) {
+
+ test_uid_to_name_one(0, "root");
+ test_uid_to_name_one(0xFFFF, "65535");
+ test_uid_to_name_one(0xFFFFFFFF, "4294967295");
+
+ test_gid_to_name_one(0, "root");
+ test_gid_to_name_one(TTY_GID, "tty");
+ test_gid_to_name_one(0xFFFF, "65535");
+ test_gid_to_name_one(0xFFFFFFFF, "4294967295");
+
+ return 0;
+}
diff --git a/src/test/test-utf8.c b/src/test/test-utf8.c
index 346f8524c6..e98be5763c 100644
--- a/src/test/test-utf8.c
+++ b/src/test/test-utf8.c
@@ -19,6 +19,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "alloc-util.h"
+#include "string-util.h"
#include "utf8.h"
#include "util.h"
diff --git a/src/test/test-util.c b/src/test/test-util.c
index 7935442dbb..f6ed55878c 100644
--- a/src/test/test-util.c
+++ b/src/test/test-util.c
@@ -20,25 +20,43 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <locale.h>
#include <errno.h>
+#include <fcntl.h>
#include <signal.h>
-#include <math.h>
+#include <string.h>
+#include <sys/types.h>
#include <sys/wait.h>
+#include <sys/xattr.h>
+#include <unistd.h>
-#include "util.h"
-#include "mkdir.h"
-#include "rm-rf.h"
-#include "strv.h"
+#include "alloc-util.h"
+#include "conf-parser.h"
+#include "cpu-set-util.h"
#include "def.h"
+#include "escape.h"
+#include "fd-util.h"
#include "fileio.h"
-#include "conf-parser.h"
-#include "virt.h"
+#include "fs-util.h"
+#include "fstab-util.h"
+#include "glob-util.h"
+#include "hexdecoct.h"
+#include "io-util.h"
+#include "mkdir.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "proc-cmdline.h"
#include "process-util.h"
+#include "rm-rf.h"
#include "signal-util.h"
+#include "special.h"
+#include "stat-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "user-util.h"
+#include "util.h"
+#include "virt.h"
+#include "web-util.h"
+#include "xattr-util.h"
static void test_streq_ptr(void) {
assert_se(streq_ptr(NULL, NULL));
@@ -218,63 +236,6 @@ static void test_close_many(void) {
unlink(name2);
}
-static void test_parse_boolean(void) {
- assert_se(parse_boolean("1") == 1);
- assert_se(parse_boolean("y") == 1);
- assert_se(parse_boolean("Y") == 1);
- assert_se(parse_boolean("yes") == 1);
- assert_se(parse_boolean("YES") == 1);
- assert_se(parse_boolean("true") == 1);
- assert_se(parse_boolean("TRUE") == 1);
- assert_se(parse_boolean("on") == 1);
- assert_se(parse_boolean("ON") == 1);
-
- assert_se(parse_boolean("0") == 0);
- assert_se(parse_boolean("n") == 0);
- assert_se(parse_boolean("N") == 0);
- assert_se(parse_boolean("no") == 0);
- assert_se(parse_boolean("NO") == 0);
- assert_se(parse_boolean("false") == 0);
- assert_se(parse_boolean("FALSE") == 0);
- assert_se(parse_boolean("off") == 0);
- assert_se(parse_boolean("OFF") == 0);
-
- assert_se(parse_boolean("garbage") < 0);
- assert_se(parse_boolean("") < 0);
- assert_se(parse_boolean("full") < 0);
-}
-
-static void test_parse_pid(void) {
- int r;
- pid_t pid;
-
- r = parse_pid("100", &pid);
- assert_se(r == 0);
- assert_se(pid == 100);
-
- r = parse_pid("0x7FFFFFFF", &pid);
- assert_se(r == 0);
- assert_se(pid == 2147483647);
-
- pid = 65; /* pid is left unchanged on ERANGE. Set to known arbitrary value. */
- r = parse_pid("0", &pid);
- assert_se(r == -ERANGE);
- assert_se(pid == 65);
-
- pid = 65; /* pid is left unchanged on ERANGE. Set to known arbitrary value. */
- r = parse_pid("-100", &pid);
- assert_se(r == -ERANGE);
- assert_se(pid == 65);
-
- pid = 65; /* pid is left unchanged on ERANGE. Set to known arbitrary value. */
- r = parse_pid("0xFFFFFFFFFFFFFFFFF", &pid);
- assert_se(r == -ERANGE);
- assert_se(pid == 65);
-
- r = parse_pid("junk", &pid);
- assert_se(r == -EINVAL);
-}
-
static void test_parse_uid(void) {
int r;
uid_t uid;
@@ -290,96 +251,6 @@ static void test_parse_uid(void) {
assert_se(r == -EINVAL);
}
-static void test_safe_atou16(void) {
- int r;
- uint16_t l;
-
- r = safe_atou16("12345", &l);
- assert_se(r == 0);
- assert_se(l == 12345);
-
- r = safe_atou16("123456", &l);
- assert_se(r == -ERANGE);
-
- r = safe_atou16("junk", &l);
- assert_se(r == -EINVAL);
-}
-
-static void test_safe_atoi16(void) {
- int r;
- int16_t l;
-
- r = safe_atoi16("-12345", &l);
- assert_se(r == 0);
- assert_se(l == -12345);
-
- r = safe_atoi16("36536", &l);
- assert_se(r == -ERANGE);
-
- r = safe_atoi16("junk", &l);
- assert_se(r == -EINVAL);
-}
-
-static void test_safe_atolli(void) {
- int r;
- long long l;
-
- r = safe_atolli("12345", &l);
- assert_se(r == 0);
- assert_se(l == 12345);
-
- r = safe_atolli("junk", &l);
- assert_se(r == -EINVAL);
-}
-
-static void test_safe_atod(void) {
- int r;
- double d;
- char *e;
-
- r = safe_atod("junk", &d);
- assert_se(r == -EINVAL);
-
- r = safe_atod("0.2244", &d);
- assert_se(r == 0);
- assert_se(fabs(d - 0.2244) < 0.000001);
-
- r = safe_atod("0,5", &d);
- assert_se(r == -EINVAL);
-
- errno = 0;
- strtod("0,5", &e);
- assert_se(*e == ',');
-
- /* Check if this really is locale independent */
- if (setlocale(LC_NUMERIC, "de_DE.utf8")) {
-
- r = safe_atod("0.2244", &d);
- assert_se(r == 0);
- assert_se(fabs(d - 0.2244) < 0.000001);
-
- r = safe_atod("0,5", &d);
- assert_se(r == -EINVAL);
-
- errno = 0;
- assert_se(fabs(strtod("0,5", &e) - 0.5) < 0.00001);
- }
-
- /* And check again, reset */
- assert_se(setlocale(LC_NUMERIC, "C"));
-
- r = safe_atod("0.2244", &d);
- assert_se(r == 0);
- assert_se(fabs(d - 0.2244) < 0.000001);
-
- r = safe_atod("0,5", &d);
- assert_se(r == -EINVAL);
-
- errno = 0;
- strtod("0,5", &e);
- assert_se(*e == ',');
-}
-
static void test_strappend(void) {
_cleanup_free_ char *t1, *t2, *t3, *t4;
@@ -892,72 +763,119 @@ static void test_protect_errno(void) {
assert_se(errno == 12);
}
-static void test_parse_size(void) {
- uint64_t bytes;
-
- assert_se(parse_size("111", 1024, &bytes) == 0);
- assert_se(bytes == 111);
-
- assert_se(parse_size("111.4", 1024, &bytes) == 0);
- assert_se(bytes == 111);
-
- assert_se(parse_size(" 112 B", 1024, &bytes) == 0);
- assert_se(bytes == 112);
-
- assert_se(parse_size(" 112.6 B", 1024, &bytes) == 0);
- assert_se(bytes == 112);
-
- assert_se(parse_size("3.5 K", 1024, &bytes) == 0);
- assert_se(bytes == 3*1024 + 512);
-
- assert_se(parse_size("3. K", 1024, &bytes) == 0);
- assert_se(bytes == 3*1024);
-
- assert_se(parse_size("3.0 K", 1024, &bytes) == 0);
- assert_se(bytes == 3*1024);
-
- assert_se(parse_size("3. 0 K", 1024, &bytes) == -EINVAL);
-
- assert_se(parse_size(" 4 M 11.5K", 1024, &bytes) == 0);
- assert_se(bytes == 4*1024*1024 + 11 * 1024 + 512);
-
- assert_se(parse_size("3B3.5G", 1024, &bytes) == -EINVAL);
-
- assert_se(parse_size("3.5G3B", 1024, &bytes) == 0);
- assert_se(bytes == 3ULL*1024*1024*1024 + 512*1024*1024 + 3);
-
- assert_se(parse_size("3.5G 4B", 1024, &bytes) == 0);
- assert_se(bytes == 3ULL*1024*1024*1024 + 512*1024*1024 + 4);
-
- assert_se(parse_size("3B3G4T", 1024, &bytes) == -EINVAL);
-
- assert_se(parse_size("4T3G3B", 1024, &bytes) == 0);
- assert_se(bytes == (4ULL*1024 + 3)*1024*1024*1024 + 3);
-
- assert_se(parse_size(" 4 T 3 G 3 B", 1024, &bytes) == 0);
- assert_se(bytes == (4ULL*1024 + 3)*1024*1024*1024 + 3);
-
- assert_se(parse_size("12P", 1024, &bytes) == 0);
- assert_se(bytes == 12ULL * 1024*1024*1024*1024*1024);
-
- assert_se(parse_size("12P12P", 1024, &bytes) == -EINVAL);
-
- assert_se(parse_size("3E 2P", 1024, &bytes) == 0);
- assert_se(bytes == (3 * 1024 + 2ULL) * 1024*1024*1024*1024*1024);
-
- assert_se(parse_size("12X", 1024, &bytes) == -EINVAL);
-
- assert_se(parse_size("12.5X", 1024, &bytes) == -EINVAL);
-
- assert_se(parse_size("12.5e3", 1024, &bytes) == -EINVAL);
-
- assert_se(parse_size("1024E", 1024, &bytes) == -ERANGE);
- assert_se(parse_size("-1", 1024, &bytes) == -ERANGE);
- assert_se(parse_size("-1024E", 1024, &bytes) == -ERANGE);
-
- assert_se(parse_size("-1024P", 1024, &bytes) == -ERANGE);
-
- assert_se(parse_size("-10B 20K", 1024, &bytes) == -ERANGE);
+static void test_parse_cpu_set(void) {
+ cpu_set_t *c = NULL;
+ int ncpus;
+ int cpu;
+
+ /* Simple range (from CPUAffinity example) */
+ ncpus = parse_cpu_set_and_warn("1 2", &c, NULL, "fake", 1, "CPUAffinity");
+ assert_se(ncpus >= 1024);
+ assert_se(CPU_ISSET_S(1, CPU_ALLOC_SIZE(ncpus), c));
+ assert_se(CPU_ISSET_S(2, CPU_ALLOC_SIZE(ncpus), c));
+ assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 2);
+ c = mfree(c);
+
+ /* A more interesting range */
+ ncpus = parse_cpu_set_and_warn("0 1 2 3 8 9 10 11", &c, NULL, "fake", 1, "CPUAffinity");
+ assert_se(ncpus >= 1024);
+ assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 8);
+ for (cpu = 0; cpu < 4; cpu++)
+ assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
+ for (cpu = 8; cpu < 12; cpu++)
+ assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
+ c = mfree(c);
+
+ /* Quoted strings */
+ ncpus = parse_cpu_set_and_warn("8 '9' 10 \"11\"", &c, NULL, "fake", 1, "CPUAffinity");
+ assert_se(ncpus >= 1024);
+ assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 4);
+ for (cpu = 8; cpu < 12; cpu++)
+ assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
+ c = mfree(c);
+
+ /* Use commas as separators */
+ ncpus = parse_cpu_set_and_warn("0,1,2,3 8,9,10,11", &c, NULL, "fake", 1, "CPUAffinity");
+ assert_se(ncpus >= 1024);
+ assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 8);
+ for (cpu = 0; cpu < 4; cpu++)
+ assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
+ for (cpu = 8; cpu < 12; cpu++)
+ assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
+ c = mfree(c);
+
+ /* Commas with spaces (and trailing comma, space) */
+ ncpus = parse_cpu_set_and_warn("0, 1, 2, 3, 4, 5, 6, 7, ", &c, NULL, "fake", 1, "CPUAffinity");
+ assert_se(ncpus >= 1024);
+ assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 8);
+ for (cpu = 0; cpu < 8; cpu++)
+ assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
+ c = mfree(c);
+
+ /* Ranges */
+ ncpus = parse_cpu_set_and_warn("0-3,8-11", &c, NULL, "fake", 1, "CPUAffinity");
+ assert_se(ncpus >= 1024);
+ assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 8);
+ for (cpu = 0; cpu < 4; cpu++)
+ assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
+ for (cpu = 8; cpu < 12; cpu++)
+ assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
+ c = mfree(c);
+
+ /* Ranges with trailing comma, space */
+ ncpus = parse_cpu_set_and_warn("0-3 8-11, ", &c, NULL, "fake", 1, "CPUAffinity");
+ assert_se(ncpus >= 1024);
+ assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 8);
+ for (cpu = 0; cpu < 4; cpu++)
+ assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
+ for (cpu = 8; cpu < 12; cpu++)
+ assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
+ c = mfree(c);
+
+ /* Negative range (returns empty cpu_set) */
+ ncpus = parse_cpu_set_and_warn("3-0", &c, NULL, "fake", 1, "CPUAffinity");
+ assert_se(ncpus >= 1024);
+ assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 0);
+ c = mfree(c);
+
+ /* Overlapping ranges */
+ ncpus = parse_cpu_set_and_warn("0-7 4-11", &c, NULL, "fake", 1, "CPUAffinity");
+ assert_se(ncpus >= 1024);
+ assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 12);
+ for (cpu = 0; cpu < 12; cpu++)
+ assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
+ c = mfree(c);
+
+ /* Mix ranges and individual CPUs */
+ ncpus = parse_cpu_set_and_warn("0,1 4-11", &c, NULL, "fake", 1, "CPUAffinity");
+ assert_se(ncpus >= 1024);
+ assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 10);
+ assert_se(CPU_ISSET_S(0, CPU_ALLOC_SIZE(ncpus), c));
+ assert_se(CPU_ISSET_S(1, CPU_ALLOC_SIZE(ncpus), c));
+ for (cpu = 4; cpu < 12; cpu++)
+ assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
+ c = mfree(c);
+
+ /* Garbage */
+ ncpus = parse_cpu_set_and_warn("0 1 2 3 garbage", &c, NULL, "fake", 1, "CPUAffinity");
+ assert_se(ncpus < 0);
+ assert_se(!c);
+
+ /* Range with garbage */
+ ncpus = parse_cpu_set_and_warn("0-3 8-garbage", &c, NULL, "fake", 1, "CPUAffinity");
+ assert_se(ncpus < 0);
+ assert_se(!c);
+
+ /* Empty string */
+ c = NULL;
+ ncpus = parse_cpu_set_and_warn("", &c, NULL, "fake", 1, "CPUAffinity");
+ assert_se(ncpus == 0); /* empty string returns 0 */
+ assert_se(!c);
+
+ /* Runnaway quoted string */
+ ncpus = parse_cpu_set_and_warn("0 1 2 3 \"4 5 6 7 ", &c, NULL, "fake", 1, "CPUAffinity");
+ assert_se(ncpus < 0);
+ assert_se(!c);
}
static void test_config_parse_iec_uint64(void) {
@@ -1493,507 +1411,6 @@ static void test_execute_directory(void) {
(void) rm_rf(template_hi, REMOVE_ROOT|REMOVE_PHYSICAL);
}
-static void test_extract_first_word(void) {
- const char *p, *original;
- char *t;
-
- p = original = "foobar waldo";
- assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
- assert_se(streq(t, "foobar"));
- free(t);
- assert_se(p == original + 7);
-
- assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
- assert_se(streq(t, "waldo"));
- free(t);
- assert_se(isempty(p));
-
- assert_se(extract_first_word(&p, &t, NULL, 0) == 0);
- assert_se(!t);
- assert_se(isempty(p));
-
- p = original = "\"foobar\" \'waldo\'";
- assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
- assert_se(streq(t, "\"foobar\""));
- free(t);
- assert_se(p == original + 9);
-
- assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
- assert_se(streq(t, "\'waldo\'"));
- free(t);
- assert_se(isempty(p));
-
- assert_se(extract_first_word(&p, &t, NULL, 0) == 0);
- assert_se(!t);
- assert_se(isempty(p));
-
- p = original = "\"foobar\" \'waldo\'";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) > 0);
- assert_se(streq(t, "foobar"));
- free(t);
- assert_se(p == original + 9);
-
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) > 0);
- assert_se(streq(t, "waldo"));
- free(t);
- assert_se(isempty(p));
-
- assert_se(extract_first_word(&p, &t, NULL, 0) == 0);
- assert_se(!t);
- assert_se(isempty(p));
-
- p = original = "\"";
- assert_se(extract_first_word(&p, &t, NULL, 0) == 1);
- assert_se(streq(t, "\""));
- free(t);
- assert_se(isempty(p));
-
- p = original = "\"";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) == -EINVAL);
- assert_se(p == original + 1);
-
- p = original = "\'";
- assert_se(extract_first_word(&p, &t, NULL, 0) == 1);
- assert_se(streq(t, "\'"));
- free(t);
- assert_se(isempty(p));
-
- p = original = "\'";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) == -EINVAL);
- assert_se(p == original + 1);
-
- p = original = "\'fooo";
- assert_se(extract_first_word(&p, &t, NULL, 0) == 1);
- assert_se(streq(t, "\'fooo"));
- free(t);
- assert_se(isempty(p));
-
- p = original = "\'fooo";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) == -EINVAL);
- assert_se(p == original + 5);
-
- p = original = "\'fooo";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_RELAX) > 0);
- assert_se(streq(t, "fooo"));
- free(t);
- assert_se(isempty(p));
-
- p = original = "yay\'foo\'bar";
- assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
- assert_se(streq(t, "yay\'foo\'bar"));
- free(t);
- assert_se(isempty(p));
-
- p = original = "yay\'foo\'bar";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) > 0);
- assert_se(streq(t, "yayfoobar"));
- free(t);
- assert_se(isempty(p));
-
- p = original = " foobar ";
- assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
- assert_se(streq(t, "foobar"));
- free(t);
- assert_se(isempty(p));
-
- p = original = " foo\\ba\\x6ar ";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE) > 0);
- assert_se(streq(t, "foo\ba\x6ar"));
- free(t);
- assert_se(isempty(p));
-
- p = original = " foo\\ba\\x6ar ";
- assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
- assert_se(streq(t, "foobax6ar"));
- free(t);
- assert_se(isempty(p));
-
- p = original = " f\\u00f6o \"pi\\U0001F4A9le\" ";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE) > 0);
- assert_se(streq(t, "föo"));
- free(t);
- assert_se(p == original + 13);
-
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE) > 0);
- assert_se(streq(t, "pi\360\237\222\251le"));
- free(t);
- assert_se(isempty(p));
-
- p = original = "fooo\\";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_RELAX) > 0);
- assert_se(streq(t, "fooo"));
- free(t);
- assert_se(isempty(p));
-
- p = original = "fooo\\";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE_RELAX) > 0);
- assert_se(streq(t, "fooo\\"));
- free(t);
- assert_se(isempty(p));
-
- p = original = "fooo\\";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE_RELAX|EXTRACT_RELAX) > 0);
- assert_se(streq(t, "fooo\\"));
- free(t);
- assert_se(isempty(p));
-
- p = original = "fooo\\";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX) > 0);
- assert_se(streq(t, "fooo\\"));
- free(t);
- assert_se(isempty(p));
-
- p = original = "\"foo\\";
- assert_se(extract_first_word(&p, &t, NULL, 0) == -EINVAL);
- assert_se(p == original + 5);
-
- p = original = "\"foo\\";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_RELAX) > 0);
- assert_se(streq(t, "foo"));
- free(t);
- assert_se(isempty(p));
-
- p = original = "foo::bar";
- assert_se(extract_first_word(&p, &t, ":", 0) == 1);
- assert_se(streq(t, "foo"));
- free(t);
- assert_se(p == original + 5);
-
- assert_se(extract_first_word(&p, &t, ":", 0) == 1);
- assert_se(streq(t, "bar"));
- free(t);
- assert_se(isempty(p));
-
- assert_se(extract_first_word(&p, &t, ":", 0) == 0);
- assert_se(!t);
- assert_se(isempty(p));
-
- p = original = "foo\\:bar::waldo";
- assert_se(extract_first_word(&p, &t, ":", 0) == 1);
- assert_se(streq(t, "foo:bar"));
- free(t);
- assert_se(p == original + 10);
-
- assert_se(extract_first_word(&p, &t, ":", 0) == 1);
- assert_se(streq(t, "waldo"));
- free(t);
- assert_se(isempty(p));
-
- assert_se(extract_first_word(&p, &t, ":", 0) == 0);
- assert_se(!t);
- assert_se(isempty(p));
-
- p = original = "\"foo\\";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE_RELAX) == -EINVAL);
- assert_se(p == original + 5);
-
- p = original = "\"foo\\";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE_RELAX|EXTRACT_RELAX) > 0);
- assert_se(streq(t, "foo\\"));
- free(t);
- assert_se(isempty(p));
-
- p = original = "\"foo\\";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX|EXTRACT_RELAX) > 0);
- assert_se(streq(t, "foo\\"));
- free(t);
- assert_se(isempty(p));
-
- p = original = "fooo\\ bar quux";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_RELAX) > 0);
- assert_se(streq(t, "fooo bar"));
- free(t);
- assert_se(p == original + 10);
-
- p = original = "fooo\\ bar quux";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE_RELAX) > 0);
- assert_se(streq(t, "fooo bar"));
- free(t);
- assert_se(p == original + 10);
-
- p = original = "fooo\\ bar quux";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE_RELAX|EXTRACT_RELAX) > 0);
- assert_se(streq(t, "fooo bar"));
- free(t);
- assert_se(p == original + 10);
-
- p = original = "fooo\\ bar quux";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE) == -EINVAL);
- assert_se(p == original + 5);
-
- p = original = "fooo\\ bar quux";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX) > 0);
- assert_se(streq(t, "fooo\\ bar"));
- free(t);
- assert_se(p == original + 10);
-
- p = original = "\\w+@\\K[\\d.]+";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE) == -EINVAL);
- assert_se(p == original + 1);
-
- p = original = "\\w+@\\K[\\d.]+";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX) > 0);
- assert_se(streq(t, "\\w+@\\K[\\d.]+"));
- free(t);
- assert_se(isempty(p));
-
- p = original = "\\w+\\b";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX) > 0);
- assert_se(streq(t, "\\w+\b"));
- free(t);
- assert_se(isempty(p));
-
- p = original = "-N ''";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) > 0);
- assert_se(streq(t, "-N"));
- free(t);
- assert_se(p == original + 3);
-
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) > 0);
- assert_se(streq(t, ""));
- free(t);
- assert_se(isempty(p));
-
- p = original = ":foo\\:bar::waldo:";
- assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 1);
- assert_se(t);
- assert_se(streq(t, ""));
- free(t);
- assert_se(p == original + 1);
-
- assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 1);
- assert_se(streq(t, "foo:bar"));
- free(t);
- assert_se(p == original + 10);
-
- assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 1);
- assert_se(t);
- assert_se(streq(t, ""));
- free(t);
- assert_se(p == original + 11);
-
- assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 1);
- assert_se(streq(t, "waldo"));
- free(t);
- assert_se(p == original + 17);
-
- assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 1);
- assert_se(streq(t, ""));
- free(t);
- assert_se(p == NULL);
-
- assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 0);
- assert_se(!t);
- assert_se(!p);
-}
-
-static void test_extract_first_word_and_warn(void) {
- const char *p, *original;
- char *t;
-
- p = original = "foobar waldo";
- assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "foobar"));
- free(t);
- assert_se(p == original + 7);
-
- assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "waldo"));
- free(t);
- assert_se(isempty(p));
-
- assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) == 0);
- assert_se(!t);
- assert_se(isempty(p));
-
- p = original = "\"foobar\" \'waldo\'";
- assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "foobar"));
- free(t);
- assert_se(p == original + 9);
-
- assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "waldo"));
- free(t);
- assert_se(isempty(p));
-
- assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) == 0);
- assert_se(!t);
- assert_se(isempty(p));
-
- p = original = "\"";
- assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES, NULL, "fake", 1, original) == -EINVAL);
- assert_se(p == original + 1);
-
- p = original = "\'";
- assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES, NULL, "fake", 1, original) == -EINVAL);
- assert_se(p == original + 1);
-
- p = original = "\'fooo";
- assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES, NULL, "fake", 1, original) == -EINVAL);
- assert_se(p == original + 5);
-
- p = original = "\'fooo";
- assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "fooo"));
- free(t);
- assert_se(isempty(p));
-
- p = original = " foo\\ba\\x6ar ";
- assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "foo\ba\x6ar"));
- free(t);
- assert_se(isempty(p));
-
- p = original = " foo\\ba\\x6ar ";
- assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "foobax6ar"));
- free(t);
- assert_se(isempty(p));
-
- p = original = " f\\u00f6o \"pi\\U0001F4A9le\" ";
- assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "föo"));
- free(t);
- assert_se(p == original + 13);
-
- assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "pi\360\237\222\251le"));
- free(t);
- assert_se(isempty(p));
-
- p = original = "fooo\\";
- assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "fooo"));
- free(t);
- assert_se(isempty(p));
-
- p = original = "fooo\\";
- assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "fooo\\"));
- free(t);
- assert_se(isempty(p));
-
- p = original = "fooo\\";
- assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "fooo\\"));
- free(t);
- assert_se(isempty(p));
-
- p = original = "\"foo\\";
- assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES, NULL, "fake", 1, original) == -EINVAL);
- assert_se(p == original + 5);
-
- p = original = "\"foo\\";
- assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "foo"));
- free(t);
- assert_se(isempty(p));
-
- p = original = "\"foo\\";
- assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE, NULL, "fake", 1, original) == -EINVAL);
- assert_se(p == original + 5);
-
- p = original = "\"foo\\";
- assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE|EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "foo"));
- free(t);
- assert_se(isempty(p));
-
- p = original = "fooo\\ bar quux";
- assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "fooo bar"));
- free(t);
- assert_se(p == original + 10);
-
- p = original = "fooo\\ bar quux";
- assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "fooo bar"));
- free(t);
- assert_se(p == original + 10);
-
- p = original = "fooo\\ bar quux";
- assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "fooo\\ bar"));
- free(t);
- assert_se(p == original + 10);
-
- p = original = "\\w+@\\K[\\d.]+";
- assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "\\w+@\\K[\\d.]+"));
- free(t);
- assert_se(isempty(p));
-
- p = original = "\\w+\\b";
- assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "\\w+\b"));
- free(t);
- assert_se(isempty(p));
-}
-
-static void test_extract_many_words(void) {
- const char *p, *original;
- char *a, *b, *c;
-
- p = original = "foobar waldi piep";
- assert_se(extract_many_words(&p, NULL, 0, &a, &b, &c, NULL) == 3);
- assert_se(isempty(p));
- assert_se(streq_ptr(a, "foobar"));
- assert_se(streq_ptr(b, "waldi"));
- assert_se(streq_ptr(c, "piep"));
- free(a);
- free(b);
- free(c);
-
- p = original = "'foobar' wa\"ld\"i ";
- assert_se(extract_many_words(&p, NULL, 0, &a, &b, &c, NULL) == 2);
- assert_se(isempty(p));
- assert_se(streq_ptr(a, "'foobar'"));
- assert_se(streq_ptr(b, "wa\"ld\"i"));
- assert_se(streq_ptr(c, NULL));
- free(a);
- free(b);
-
- p = original = "'foobar' wa\"ld\"i ";
- assert_se(extract_many_words(&p, NULL, EXTRACT_QUOTES, &a, &b, &c, NULL) == 2);
- assert_se(isempty(p));
- assert_se(streq_ptr(a, "foobar"));
- assert_se(streq_ptr(b, "waldi"));
- assert_se(streq_ptr(c, NULL));
- free(a);
- free(b);
-
- p = original = "";
- assert_se(extract_many_words(&p, NULL, 0, &a, &b, &c, NULL) == 0);
- assert_se(isempty(p));
- assert_se(streq_ptr(a, NULL));
- assert_se(streq_ptr(b, NULL));
- assert_se(streq_ptr(c, NULL));
-
- p = original = " ";
- assert_se(extract_many_words(&p, NULL, 0, &a, &b, &c, NULL) == 0);
- assert_se(isempty(p));
- assert_se(streq_ptr(a, NULL));
- assert_se(streq_ptr(b, NULL));
- assert_se(streq_ptr(c, NULL));
-
- p = original = "foobar";
- assert_se(extract_many_words(&p, NULL, 0, NULL) == 0);
- assert_se(p == original);
-
- p = original = "foobar waldi";
- assert_se(extract_many_words(&p, NULL, 0, &a, NULL) == 1);
- assert_se(p == original+7);
- assert_se(streq_ptr(a, "foobar"));
- free(a);
-
- p = original = " foobar ";
- assert_se(extract_many_words(&p, NULL, 0, &a, NULL) == 1);
- assert_se(isempty(p));
- assert_se(streq_ptr(a, "foobar"));
- free(a);
-}
-
static int parse_item(const char *key, const char *value) {
assert_se(key);
@@ -2142,20 +1559,6 @@ static void test_shell_maybe_quote(void) {
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);
-}
-
static void test_tempfn(void) {
char *ret = NULL, *p;
@@ -2204,6 +1607,44 @@ static void test_strcmp_ptr(void) {
assert_se(strcmp_ptr("", "") == 0);
}
+static void test_fgetxattrat_fake(void) {
+ char t[] = "/var/tmp/xattrtestXXXXXX";
+ _cleanup_close_ int fd = -1;
+ const char *x;
+ char v[3] = {};
+ int r;
+
+ assert_se(mkdtemp(t));
+ x = strjoina(t, "/test");
+ assert_se(touch(x) >= 0);
+
+ r = setxattr(x, "user.foo", "bar", 3, 0);
+ if (r < 0 && errno == EOPNOTSUPP) /* no xattrs supported on /var/tmp... */
+ goto cleanup;
+ assert_se(r >= 0);
+
+ fd = open(t, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY);
+ assert_se(fd >= 0);
+
+ assert_se(fgetxattrat_fake(fd, "test", "user.foo", v, 3, 0) >= 0);
+ assert_se(memcmp(v, "bar", 3) == 0);
+
+ safe_close(fd);
+ fd = open("/", O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY);
+ assert_se(fd >= 0);
+ assert_se(fgetxattrat_fake(fd, "usr", "user.idontexist", v, 3, 0) == -ENODATA);
+
+cleanup:
+ assert_se(unlink(x) >= 0);
+ assert_se(rmdir(t) >= 0);
+}
+
+static void test_runlevel_to_target(void) {
+ assert_se(streq_ptr(runlevel_to_target(NULL), NULL));
+ assert_se(streq_ptr(runlevel_to_target("unknown-runlevel"), NULL));
+ assert_se(streq_ptr(runlevel_to_target("3"), SPECIAL_MULTI_USER_TARGET));
+}
+
int main(int argc, char *argv[]) {
log_parse_environment();
log_open();
@@ -2216,13 +1657,7 @@ int main(int argc, char *argv[]) {
test_div_round_up();
test_first_word();
test_close_many();
- test_parse_boolean();
- test_parse_pid();
test_parse_uid();
- test_safe_atou16();
- test_safe_atoi16();
- test_safe_atolli();
- test_safe_atod();
test_strappend();
test_strstrip();
test_delete_chars();
@@ -2249,7 +1684,7 @@ int main(int argc, char *argv[]) {
test_memdup_multiply();
test_u64log2();
test_protect_errno();
- test_parse_size();
+ test_parse_cpu_set();
test_config_parse_iec_uint64();
test_strextend();
test_strrep();
@@ -2280,9 +1715,6 @@ int main(int argc, char *argv[]) {
test_search_and_fopen_nulstr();
test_glob_exists();
test_execute_directory();
- test_extract_first_word();
- test_extract_first_word_and_warn();
- test_extract_many_words();
test_parse_proc_cmdline();
test_raw_clone();
test_same_fd();
@@ -2290,9 +1722,10 @@ int main(int argc, char *argv[]) {
test_sparse_write();
test_shell_escape();
test_shell_maybe_quote();
- test_parse_mode();
test_tempfn();
test_strcmp_ptr();
+ test_fgetxattrat_fake();
+ test_runlevel_to_target();
return 0;
}
diff --git a/src/test/test-watchdog.c b/src/test/test-watchdog.c
index 2e5d0c3aae..d10d9f49af 100644
--- a/src/test/test-watchdog.c
+++ b/src/test/test-watchdog.c
@@ -21,8 +21,8 @@
#include <unistd.h>
-#include "watchdog.h"
#include "log.h"
+#include "watchdog.h"
int main(int argc, char *argv[]) {
usec_t t = 10 * USEC_PER_SEC;
diff --git a/src/test/test-xml.c b/src/test/test-xml.c
index ea109fbde0..548d75a3c3 100644
--- a/src/test/test-xml.c
+++ b/src/test/test-xml.c
@@ -21,8 +21,10 @@
#include <stdarg.h>
-#include "xml.h"
+#include "alloc-util.h"
+#include "string-util.h"
#include "util.h"
+#include "xml.h"
static void test_one(const char *data, ...) {
void *state = NULL;
diff --git a/src/timedate/timedatectl.c b/src/timedate/timedatectl.c
index 12a7ed6718..b6fa458696 100644
--- a/src/timedate/timedatectl.c
+++ b/src/timedate/timedatectl.c
@@ -20,20 +20,21 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
-#include <stdbool.h>
#include <getopt.h>
#include <locale.h>
+#include <stdbool.h>
+#include <stdlib.h>
#include "sd-bus.h"
-#include "bus-util.h"
+
#include "bus-error.h"
-#include "util.h"
+#include "bus-util.h"
+#include "pager.h"
+#include "parse-util.h"
#include "spawn-polkit-agent.h"
-#include "build.h"
#include "strv.h"
-#include "pager.h"
#include "terminal-util.h"
+#include "util.h"
static bool arg_no_pager = false;
static bool arg_ask_password = true;
@@ -192,7 +193,7 @@ static int show_status(sd_bus *bus, char **args, unsigned n) {
}
static int set_time(sd_bus *bus, char **args, unsigned n) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
bool relative = false, interactive = arg_ask_password;
usec_t t;
int r;
@@ -223,7 +224,7 @@ static int set_time(sd_bus *bus, char **args, unsigned n) {
}
static int set_timezone(sd_bus *bus, char **args, unsigned n) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert(args);
@@ -246,7 +247,7 @@ static int set_timezone(sd_bus *bus, char **args, unsigned n) {
}
static int set_local_rtc(sd_bus *bus, char **args, unsigned n) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r, b;
assert(args);
@@ -275,7 +276,7 @@ static int set_local_rtc(sd_bus *bus, char **args, unsigned n) {
}
static int set_ntp(sd_bus *bus, char **args, unsigned n) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int b, r;
assert(args);
@@ -374,9 +375,7 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case 'H':
arg_transport = BUS_TRANSPORT_REMOTE;
@@ -491,7 +490,7 @@ static int timedatectl_main(sd_bus *bus, int argc, char *argv[]) {
}
int main(int argc, char *argv[]) {
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
setlocale(LC_ALL, "");
@@ -502,7 +501,7 @@ int main(int argc, char *argv[]) {
if (r <= 0)
goto finish;
- r = bus_open_transport(arg_transport, arg_host, false, &bus);
+ r = bus_connect_transport(arg_transport, arg_host, false, &bus);
if (r < 0) {
log_error_errno(r, "Failed to create bus connection: %m");
goto finish;
diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c
index 6de9e246f6..bfe1b5e8cd 100644
--- a/src/timedate/timedated.c
+++ b/src/timedate/timedated.c
@@ -23,21 +23,23 @@
#include <string.h>
#include <unistd.h>
-#include "sd-messages.h"
-#include "sd-event.h"
#include "sd-bus.h"
+#include "sd-event.h"
+#include "sd-messages.h"
-#include "util.h"
-#include "strv.h"
-#include "def.h"
+#include "alloc-util.h"
+#include "bus-common-errors.h"
+#include "bus-error.h"
+#include "bus-util.h"
#include "clock-util.h"
-#include "path-util.h"
+#include "def.h"
#include "fileio-label.h"
-#include "bus-util.h"
-#include "bus-error.h"
-#include "bus-common-errors.h"
-#include "event-util.h"
+#include "fs-util.h"
+#include "path-util.h"
#include "selinux-util.h"
+#include "strv.h"
+#include "user-util.h"
+#include "util.h"
#define NULL_ADJTIME_UTC "0.0 0 0\n0\nUTC\n"
#define NULL_ADJTIME_LOCAL "0.0 0 0\n0\nLOCAL\n"
@@ -164,8 +166,8 @@ 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;
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
const char *s;
int r;
@@ -647,7 +649,7 @@ static const sd_bus_vtable timedate_vtable[] = {
};
static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
assert(c);
@@ -678,8 +680,8 @@ static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
int main(int argc, char *argv[]) {
Context context = {};
- _cleanup_event_unref_ sd_event *event = NULL;
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
log_set_target(LOG_TARGET_AUTO);
diff --git a/src/timesync/timesyncd-conf.c b/src/timesync/timesyncd-conf.c
index df4d89a620..5881bc0c45 100644
--- a/src/timesync/timesyncd-conf.c
+++ b/src/timesync/timesyncd-conf.c
@@ -19,14 +19,15 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-
+#include "alloc-util.h"
+#include "def.h"
+#include "extract-word.h"
+#include "string-util.h"
+#include "timesyncd-conf.h"
#include "timesyncd-manager.h"
#include "timesyncd-server.h"
-#include "timesyncd-conf.h"
int manager_parse_server_string(Manager *m, ServerType type, const char *string) {
- const char *word, *state;
- size_t length;
ServerName *first;
int r;
@@ -35,17 +36,20 @@ int manager_parse_server_string(Manager *m, ServerType type, const char *string)
first = type == SERVER_FALLBACK ? m->fallback_servers : m->system_servers;
- FOREACH_WORD_QUOTED(word, length, string, state) {
- char buffer[length+1];
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
bool found = false;
ServerName *n;
- memcpy(buffer, word, length);
- buffer[length] = 0;
+ r = extract_first_word(&string, &word, NULL, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse timesyncd server syntax \"%s\": %m", string);
+ if (r == 0)
+ break;
/* Filter out duplicates */
LIST_FOREACH(names, n, first)
- if (streq_ptr(n->string, buffer)) {
+ if (streq_ptr(n->string, word)) {
found = true;
break;
}
@@ -53,7 +57,7 @@ int manager_parse_server_string(Manager *m, ServerType type, const char *string)
if (found)
continue;
- r = server_name_new(m, NULL, type, buffer);
+ r = server_name_new(m, NULL, type, word);
if (r < 0)
return r;
}
@@ -85,7 +89,7 @@ int config_parse_servers(
else {
r = manager_parse_server_string(m, ltype, rvalue);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to parse NTP server string '%s'. Ignoring.", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse NTP server string '%s'. Ignoring.", rvalue);
return 0;
}
}
@@ -96,8 +100,8 @@ int config_parse_servers(
int manager_parse_config_file(Manager *m) {
assert(m);
- return config_parse_many("/etc/systemd/timesyncd.conf",
- CONF_DIRS_NULSTR("systemd/timesyncd.conf"),
+ return config_parse_many(PKGSYSCONFDIR "/timesyncd.conf",
+ CONF_PATHS_NULSTR("systemd/timesyncd.conf.d"),
"Time\0",
config_item_perf_lookup, timesyncd_gperf_lookup,
false, m);
diff --git a/src/timesync/timesyncd-conf.h b/src/timesync/timesyncd-conf.h
index 56466fe462..cbc19c4054 100644
--- a/src/timesync/timesyncd-conf.h
+++ b/src/timesync/timesyncd-conf.h
@@ -22,7 +22,6 @@
***/
#include "conf-parser.h"
-
#include "timesyncd-manager.h"
const struct ConfigPerfItem* timesyncd_gperf_lookup(const char *key, unsigned length);
diff --git a/src/timesync/timesyncd-manager.c b/src/timesync/timesyncd-manager.c
index 40e0fd31fe..5627d17de1 100644
--- a/src/timesync/timesyncd-manager.c
+++ b/src/timesync/timesyncd-manager.c
@@ -19,31 +19,36 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
#include <errno.h>
-#include <time.h>
#include <math.h>
#include <netinet/in.h>
#include <netinet/ip.h>
+#include <resolv.h>
+#include <stdlib.h>
+#include <sys/socket.h>
#include <sys/timerfd.h>
#include <sys/timex.h>
-#include <sys/socket.h>
-#include <resolv.h>
#include <sys/types.h>
+#include <time.h>
-#include "missing.h"
-#include "util.h"
-#include "sparse-endian.h"
-#include "log.h"
-#include "socket-util.h"
+#include "sd-daemon.h"
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fs-util.h"
#include "list.h"
+#include "log.h"
+#include "missing.h"
+#include "network-util.h"
#include "ratelimit.h"
+#include "socket-util.h"
+#include "sparse-endian.h"
+#include "string-util.h"
#include "strv.h"
-#include "sd-daemon.h"
-#include "network-util.h"
+#include "time-util.h"
#include "timesyncd-conf.h"
#include "timesyncd-manager.h"
-#include "time-util.h"
+#include "util.h"
#ifndef ADJ_SETOFFSET
#define ADJ_SETOFFSET 0x0100 /* add 'time' to current time */
@@ -365,9 +370,10 @@ static int manager_adjust_clock(Manager *m, double offset, int leap_sec) {
r = clock_adjtime(CLOCK_REALTIME, &tmx);
if (r < 0)
- return r;
+ return -errno;
- touch("/var/lib/systemd/clock");
+ /* If touch fails, there isn't much we can do. Maybe it'll work next time. */
+ (void) touch("/var/lib/systemd/clock");
m->drift_ppm = tmx.freq / 65536;
@@ -662,7 +668,7 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re
m->sync = true;
r = manager_adjust_clock(m, offset, leap_sec);
if (r < 0)
- log_error_errno(errno, "Failed to call clock_adjtime(): %m");
+ log_error_errno(r, "Failed to call clock_adjtime(): %m");
}
log_debug("interval/delta/delay/jitter/drift " USEC_FMT "s/%+.3fs/%.3fs/%.3fs/%+ippm%s",
diff --git a/src/timesync/timesyncd-manager.h b/src/timesync/timesyncd-manager.h
index 090b2fcba8..fab22cfe84 100644
--- a/src/timesync/timesyncd-manager.h
+++ b/src/timesync/timesyncd-manager.h
@@ -22,8 +22,9 @@
***/
#include "sd-event.h"
-#include "sd-resolve.h"
#include "sd-network.h"
+#include "sd-resolve.h"
+
#include "list.h"
#include "ratelimit.h"
diff --git a/src/timesync/timesyncd-server.c b/src/timesync/timesyncd-server.c
index ec3fe1fc4e..f98e6b4cf0 100644
--- a/src/timesync/timesyncd-server.c
+++ b/src/timesync/timesyncd-server.c
@@ -19,6 +19,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "alloc-util.h"
#include "timesyncd-server.h"
int server_address_new(
diff --git a/src/timesync/timesyncd-server.h b/src/timesync/timesyncd-server.h
index 18c44445e1..f764d0737b 100644
--- a/src/timesync/timesyncd-server.h
+++ b/src/timesync/timesyncd-server.h
@@ -21,8 +21,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "socket-util.h"
#include "list.h"
+#include "socket-util.h"
typedef struct ServerAddress ServerAddress;
typedef struct ServerName ServerName;
diff --git a/src/timesync/timesyncd.c b/src/timesync/timesyncd.c
index 3cb7d435cd..7f70eaaea0 100644
--- a/src/timesync/timesyncd.c
+++ b/src/timesync/timesyncd.c
@@ -19,15 +19,18 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "sd-event.h"
#include "sd-daemon.h"
-#include "capability.h"
+#include "sd-event.h"
+
+#include "capability-util.h"
#include "clock-util.h"
+#include "fd-util.h"
+#include "fs-util.h"
#include "network-util.h"
#include "signal-util.h"
-
-#include "timesyncd-manager.h"
#include "timesyncd-conf.h"
+#include "timesyncd-manager.h"
+#include "user-util.h"
static int load_clock_timestamp(uid_t uid, gid_t gid) {
_cleanup_close_ int fd = -1;
@@ -57,12 +60,12 @@ static int load_clock_timestamp(uid_t uid, gid_t gid) {
/* Try to fix the access mode, so that we can still
touch the file after dropping priviliges */
- fchmod(fd, 0644);
- fchown(fd, uid, gid);
+ (void) fchmod(fd, 0644);
+ (void) fchown(fd, uid, gid);
} else
/* create stamp file with the compiled-in date */
- touch_file("/var/lib/systemd/clock", true, min, uid, gid, 0644);
+ (void) touch_file("/var/lib/systemd/clock", true, min, uid, gid, 0644);
ct = now(CLOCK_REALTIME);
if (ct < min) {
@@ -150,7 +153,7 @@ int main(int argc, char *argv[]) {
/* if we got an authoritative time, store it in the file system */
if (m->sync)
- touch("/var/lib/systemd/clock");
+ (void) touch("/var/lib/systemd/clock");
sd_event_get_exit_code(m->event, &r);
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index 8f29256c6d..bb81ff5e3a 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -20,43 +20,59 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
-#include <fcntl.h>
+#include <dirent.h>
#include <errno.h>
-#include <string.h>
+#include <fcntl.h>
+#include <fnmatch.h>
+#include <getopt.h>
+#include <glob.h>
#include <limits.h>
-#include <dirent.h>
+#include <linux/fs.h>
+#include <stdbool.h>
+#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
-#include <stddef.h>
-#include <getopt.h>
-#include <stdbool.h>
-#include <time.h>
-#include <glob.h>
-#include <fnmatch.h>
+#include <string.h>
#include <sys/stat.h>
#include <sys/xattr.h>
-#include <linux/fs.h>
+#include <time.h>
+#include <unistd.h>
+#include "acl-util.h"
+#include "alloc-util.h"
+#include "btrfs-util.h"
+#include "capability-util.h"
+#include "chattr-util.h"
+#include "conf-files.h"
+#include "copy.h"
+#include "def.h"
+#include "escape.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "formats-util.h"
+#include "fs-util.h"
+#include "glob-util.h"
+#include "io-util.h"
+#include "label.h"
#include "log.h"
-#include "util.h"
#include "macro.h"
#include "missing.h"
#include "mkdir.h"
+#include "mount-util.h"
+#include "parse-util.h"
#include "path-util.h"
-#include "strv.h"
-#include "label.h"
-#include "set.h"
-#include "conf-files.h"
-#include "capability.h"
-#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"
+#include "set.h"
+#include "specifier.h"
+#include "stat-util.h"
+#include "stdio-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "strv.h"
+#include "umask-util.h"
+#include "user-util.h"
+#include "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
@@ -70,6 +86,8 @@ typedef enum ItemType {
CREATE_DIRECTORY = 'd',
TRUNCATE_DIRECTORY = 'D',
CREATE_SUBVOLUME = 'v',
+ CREATE_SUBVOLUME_INHERIT_QUOTA = 'q',
+ CREATE_SUBVOLUME_NEW_QUOTA = 'Q',
CREATE_FIFO = 'p',
CREATE_SYMLINK = 'L',
CREATE_CHAR_DEVICE = 'c',
@@ -141,7 +159,7 @@ static char **arg_include_prefixes = NULL;
static char **arg_exclude_prefixes = NULL;
static char *arg_root = NULL;
-static const char conf_file_dirs[] = CONF_DIRS_NULSTR("tmpfiles");
+static const char conf_file_dirs[] = CONF_PATHS_NULSTR("tmpfiles.d");
#define MAX_DEPTH 256
@@ -181,6 +199,8 @@ static bool takes_ownership(ItemType t) {
CREATE_DIRECTORY,
TRUNCATE_DIRECTORY,
CREATE_SUBVOLUME,
+ CREATE_SUBVOLUME_INHERIT_QUOTA,
+ CREATE_SUBVOLUME_NEW_QUOTA,
CREATE_FIFO,
CREATE_SYMLINK,
CREATE_CHAR_DEVICE,
@@ -1055,7 +1075,7 @@ static int item_do_children(Item *i, const char *path, action_t action) {
errno = 0;
de = readdir(d);
if (!de) {
- if (errno != 0 && r == 0)
+ if (errno > 0 && r == 0)
r = -errno;
break;
@@ -1199,16 +1219,36 @@ static int create_item(Item *i) {
case CREATE_DIRECTORY:
case TRUNCATE_DIRECTORY:
case CREATE_SUBVOLUME:
+ case CREATE_SUBVOLUME_INHERIT_QUOTA:
+ case CREATE_SUBVOLUME_NEW_QUOTA:
RUN_WITH_UMASK(0000)
mkdir_parents_label(i->path, 0755);
- if (i->type == CREATE_SUBVOLUME)
- RUN_WITH_UMASK((~i->mode) & 0777) {
- r = btrfs_subvol_make(i->path);
- log_debug_errno(r, "Creating subvolume \"%s\": %m", i->path);
+ if (IN_SET(i->type, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA)) {
+
+ if (btrfs_is_subvol(isempty(arg_root) ? "/" : arg_root) <= 0)
+
+ /* Don't create a subvolume unless the
+ * root directory is one, too. We do
+ * this under the assumption that if
+ * the root directory is just a plain
+ * directory (i.e. very light-weight),
+ * we shouldn't try to split it up
+ * into subvolumes (i.e. more
+ * heavy-weight). Thus, chroot()
+ * environments and suchlike will get
+ * a full brtfs subvolume set up below
+ * their tree only if they
+ * specifically set up a btrfs
+ * subvolume for the root dir too. */
+
+ r = -ENOTTY;
+ else {
+ RUN_WITH_UMASK((~i->mode) & 0777)
+ r = btrfs_subvol_make(i->path);
}
- else
+ } else
r = 0;
if (IN_SET(i->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY) || r == -ENOTTY)
@@ -1237,6 +1277,28 @@ static int create_item(Item *i) {
log_debug("%s directory \"%s\".", creation_mode_verb_to_string(creation), i->path);
+ if (IN_SET(i->type, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA)) {
+ r = btrfs_subvol_auto_qgroup(i->path, 0, i->type == CREATE_SUBVOLUME_NEW_QUOTA);
+ if (r == -ENOTTY) {
+ log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" because of unsupported file system or because directory is not a subvolume: %m", i->path);
+ return 0;
+ }
+ if (r == -EROFS) {
+ log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" because of read-only file system: %m", i->path);
+ return 0;
+ }
+ if (r == -ENOPROTOOPT) {
+ log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" because quota support is disabled: %m", i->path);
+ return 0;
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to adjust quota for subvolume \"%s\": %m", i->path);
+ if (r > 0)
+ log_debug("Adjusted quota for subvolume \"%s\".", i->path);
+ if (r == 0)
+ log_debug("Quota for subvolume \"%s\" already in place, no change made.", i->path);
+ }
+
r = path_set_perms(i, i->path);
if (r < 0)
return r;
@@ -1493,6 +1555,8 @@ static int remove_item(Item *i) {
case TRUNCATE_FILE:
case CREATE_DIRECTORY:
case CREATE_SUBVOLUME:
+ case CREATE_SUBVOLUME_INHERIT_QUOTA:
+ case CREATE_SUBVOLUME_NEW_QUOTA:
case CREATE_FIFO:
case CREATE_SYMLINK:
case CREATE_CHAR_DEVICE:
@@ -1562,8 +1626,7 @@ static int clean_item_instance(Item *i, const char* instance) {
if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0)
return log_error_errno(errno, "stat(%s/..) failed: %m", i->path);
- mountpoint = s.st_dev != ps.st_dev ||
- (s.st_dev == ps.st_dev && s.st_ino == ps.st_ino);
+ mountpoint = s.st_dev != ps.st_dev || s.st_ino == ps.st_ino;
log_debug("Cleanup threshold for %s \"%s\" is %s",
mountpoint ? "mount point" : "directory",
@@ -1584,6 +1647,8 @@ static int clean_item(Item *i) {
switch (i->type) {
case CREATE_DIRECTORY:
case CREATE_SUBVOLUME:
+ case CREATE_SUBVOLUME_INHERIT_QUOTA:
+ case CREATE_SUBVOLUME_NEW_QUOTA:
case TRUNCATE_DIRECTORY:
case IGNORE_PATH:
case COPY_FILES:
@@ -1820,6 +1885,8 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
case CREATE_DIRECTORY:
case CREATE_SUBVOLUME:
+ case CREATE_SUBVOLUME_INHERIT_QUOTA:
+ case CREATE_SUBVOLUME_NEW_QUOTA:
case TRUNCATE_DIRECTORY:
case CREATE_FIFO:
case IGNORE_PATH:
@@ -1984,8 +2051,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
i.mode = m;
i.mode_set = true;
} else
- i.mode = IN_SET(i.type, CREATE_DIRECTORY, CREATE_SUBVOLUME, TRUNCATE_DIRECTORY)
- ? 0755 : 0644;
+ i.mode = IN_SET(i.type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA) ? 0755 : 0644;
if (!isempty(age) && !streq(age, "-")) {
const char *a = age;
@@ -2076,7 +2142,7 @@ static int parse_argv(int argc, char *argv[]) {
{}
};
- int c;
+ int c, r;
assert(argc >= 0);
assert(argv);
@@ -2090,9 +2156,7 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case ARG_CREATE:
arg_create = true;
@@ -2121,12 +2185,9 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_ROOT:
- free(arg_root);
- arg_root = path_make_absolute_cwd(optarg);
- if (!arg_root)
- return log_oom();
-
- path_kill_slashes(arg_root);
+ r = parse_path_argument_and_warn(optarg, true, &arg_root);
+ if (r < 0)
+ return r;
break;
case '?':
@@ -2189,7 +2250,7 @@ static int read_config_file(const char *fn, bool ignore_enoent) {
continue;
ORDERED_HASHMAP_FOREACH(j, items, iter) {
- if (j->type != CREATE_DIRECTORY && j->type != TRUNCATE_DIRECTORY && j->type != CREATE_SUBVOLUME)
+ if (!IN_SET(j->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA))
continue;
if (path_equal(j->path, i->path)) {
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 82cbf95f1e..8cfe10330d 100644
--- a/src/tty-ask-password-agent/tty-ask-password-agent.c
+++ b/src/tty-ask-password-agent/tty-ask-password-agent.c
@@ -19,32 +19,36 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdbool.h>
#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <poll.h>
+#include <stdbool.h>
+#include <stddef.h>
#include <string.h>
+#include <sys/inotify.h>
+#include <sys/signalfd.h>
#include <sys/socket.h>
#include <sys/un.h>
-#include <stddef.h>
-#include <poll.h>
-#include <sys/inotify.h>
#include <unistd.h>
-#include <getopt.h>
-#include <sys/signalfd.h>
-#include <fcntl.h>
-#include "util.h"
+#include "alloc-util.h"
+#include "ask-password-api.h"
+#include "conf-parser.h"
+#include "def.h"
+#include "dirent-util.h"
+#include "fd-util.h"
+#include "io-util.h"
#include "mkdir.h"
#include "path-util.h"
-#include "conf-parser.h"
-#include "utmp-wtmp.h"
+#include "process-util.h"
+#include "signal-util.h"
#include "socket-util.h"
-#include "ask-password-api.h"
+#include "string-util.h"
#include "strv.h"
-#include "build.h"
-#include "def.h"
-#include "process-util.h"
#include "terminal-util.h"
-#include "signal-util.h"
+#include "util.h"
+#include "utmp-wtmp.h"
static enum {
ACTION_LIST,
@@ -59,9 +63,9 @@ static bool arg_console = false;
static int ask_password_plymouth(
const char *message,
usec_t until,
+ AskPasswordFlags flags,
const char *flag_file,
- bool accept_cached,
- char ***_passphrases) {
+ char ***ret) {
_cleanup_close_ int fd = -1, notify = -1;
union sockaddr_union sa = PLYMOUTH_SOCKET;
@@ -76,7 +80,7 @@ static int ask_password_plymouth(
POLL_INOTIFY
};
- assert(_passphrases);
+ assert(ret);
if (flag_file) {
notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
@@ -94,17 +98,15 @@ static int ask_password_plymouth(
r = connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1));
if (r < 0)
- return log_error_errno(errno, "Failed to connect to Plymouth: %m");
+ return -errno;
- if (accept_cached) {
+ if (flags & ASK_PASSWORD_ACCEPT_CACHED) {
packet = strdup("c");
n = 1;
- } else if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1),
- message, &n) < 0)
+ } else if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0)
packet = NULL;
-
if (!packet)
- return log_oom();
+ return -ENOMEM;
r = loop_write(fd, packet, n + 1, true);
if (r < 0)
@@ -123,33 +125,48 @@ static int ask_password_plymouth(
y = now(CLOCK_MONOTONIC);
- if (y > until)
- return -ETIME;
+ if (y > until) {
+ r = -ETIME;
+ goto finish;
+ }
sleep_for = (int) ((until - y) / USEC_PER_MSEC);
}
- if (flag_file && access(flag_file, F_OK) < 0)
- return -errno;
+ if (flag_file && access(flag_file, F_OK) < 0) {
+ r = -errno;
+ goto finish;
+ }
- j = poll(pollfd, notify > 0 ? 2 : 1, sleep_for);
+ j = poll(pollfd, notify >= 0 ? 2 : 1, sleep_for);
if (j < 0) {
if (errno == EINTR)
continue;
- return -errno;
- } else if (j == 0)
- return -ETIME;
+ r = -errno;
+ goto finish;
+ } else if (j == 0) {
+ r = -ETIME;
+ goto finish;
+ }
- if (notify > 0 && pollfd[POLL_INOTIFY].revents != 0)
+ if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0)
flush_fd(notify);
if (pollfd[POLL_SOCKET].revents == 0)
continue;
k = read(fd, buffer + p, sizeof(buffer) - p);
- if (k <= 0)
- return r = k < 0 ? -errno : -EIO;
+ if (k < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+
+ r = -errno;
+ goto finish;
+ } else if (k == 0) {
+ r = -EIO;
+ goto finish;
+ }
p += k;
@@ -158,26 +175,29 @@ static int ask_password_plymouth(
if (buffer[0] == 5) {
- if (accept_cached) {
+ if (flags & ASK_PASSWORD_ACCEPT_CACHED) {
/* Hmm, first try with cached
* passwords failed, so let's retry
* with a normal password request */
packet = mfree(packet);
- if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0)
- return -ENOMEM;
+ if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0) {
+ r = -ENOMEM;
+ goto finish;
+ }
r = loop_write(fd, packet, n+1, true);
if (r < 0)
- return r;
+ goto finish;
- accept_cached = false;
+ flags &= ~ASK_PASSWORD_ACCEPT_CACHED;
p = 0;
continue;
}
/* No password, because UI not shown */
- return -ENOENT;
+ r = -ENOENT;
+ goto finish;
} else if (buffer[0] == 2 || buffer[0] == 9) {
uint32_t size;
@@ -189,32 +209,43 @@ static int ask_password_plymouth(
memcpy(&size, buffer+1, sizeof(size));
size = le32toh(size);
- if (size + 5 > sizeof(buffer))
- return -EIO;
+ if (size + 5 > sizeof(buffer)) {
+ r = -EIO;
+ goto finish;
+ }
if (p-5 < size)
continue;
l = strv_parse_nulstr(buffer + 5, size);
- if (!l)
- return -ENOMEM;
+ if (!l) {
+ r = -ENOMEM;
+ goto finish;
+ }
- *_passphrases = l;
+ *ret = l;
break;
- } else
+ } else {
/* Unknown packet */
- return -EIO;
+ r = -EIO;
+ goto finish;
+ }
}
- return 0;
+ r = 0;
+
+finish:
+ memory_erase(buffer, sizeof(buffer));
+ return r;
}
static int parse_password(const char *filename, char **wall) {
_cleanup_free_ char *socket_name = NULL, *message = NULL, *packet = NULL;
+ bool accept_cached = false, echo = false;
+ size_t packet_length = 0;
uint64_t not_after = 0;
unsigned pid = 0;
- bool accept_cached = false, echo = false;
const ConfigTableItem items[] = {
{ "Ask", "Socket", config_parse_string, 0, &socket_name },
@@ -257,7 +288,7 @@ static int parse_password(const char *filename, char **wall) {
if (asprintf(&_wall,
"%s%sPassword entry required for \'%s\' (PID %u).\r\n"
"Please enter password with the systemd-tty-ask-password-agent tool!",
- *wall ? *wall : "",
+ strempty(*wall),
*wall ? "\r\n\r\n" : "",
message,
pid) < 0)
@@ -268,7 +299,6 @@ static int parse_password(const char *filename, char **wall) {
} else {
union sockaddr_union sa = {};
- size_t packet_length = 0;
_cleanup_close_ int socket_fd = -1;
assert(arg_action == ACTION_QUERY ||
@@ -282,9 +312,9 @@ static int parse_password(const char *filename, char **wall) {
}
if (arg_plymouth) {
- _cleanup_strv_free_ char **passwords = NULL;
+ _cleanup_strv_free_erase_ char **passwords = NULL;
- r = ask_password_plymouth(message, not_after, filename, accept_cached, &passwords);
+ r = ask_password_plymouth(message, not_after, accept_cached ? ASK_PASSWORD_ACCEPT_CACHED : 0, filename, &passwords);
if (r >= 0) {
char **p;
@@ -306,19 +336,23 @@ static int parse_password(const char *filename, char **wall) {
}
} else {
+ _cleanup_string_free_erase_ char *password = NULL;
int tty_fd = -1;
- _cleanup_free_ char *password = NULL;
if (arg_console) {
tty_fd = acquire_terminal("/dev/console", false, false, false, USEC_INFINITY);
if (tty_fd < 0)
- return tty_fd;
+ return log_error_errno(tty_fd, "Failed to acquire /dev/console: %m");
+
+ r = reset_terminal_fd(tty_fd, true);
+ if (r < 0)
+ log_warning_errno(r, "Failed to reset terminal, ignoring: %m");
}
- r = ask_password_tty(message, not_after, echo, filename, &password);
+ r = ask_password_tty(message, NULL, not_after, echo ? ASK_PASSWORD_ECHO : 0, filename, &password);
if (arg_console) {
- safe_close(tty_fd);
+ tty_fd = safe_close(tty_fd);
release_terminal();
}
@@ -334,67 +368,79 @@ static int parse_password(const char *filename, char **wall) {
}
}
- if (IN_SET(r, -ETIME, -ENOENT))
+ if (IN_SET(r, -ETIME, -ENOENT)) {
/* If the query went away, that's OK */
- return 0;
-
- if (r < 0)
- return log_error_errno(r, "Failed to query password: %m");
+ r = 0;
+ goto finish;
+ }
+ if (r < 0) {
+ log_error_errno(r, "Failed to query password: %m");
+ goto finish;
+ }
socket_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
- if (socket_fd < 0)
- return log_error_errno(errno, "socket(): %m");
+ if (socket_fd < 0) {
+ r = log_error_errno(errno, "socket(): %m");
+ goto finish;
+ }
sa.un.sun_family = AF_UNIX;
strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path));
- r = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa,
- offsetof(struct sockaddr_un, sun_path) + strlen(socket_name));
- if (r < 0) {
- log_error_errno(errno, "Failed to send: %m");
- return r;
- }
+ r = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(socket_name));
+ memory_erase(packet, packet_length);
+ if (r < 0)
+ return log_error_errno(errno, "Failed to send: %m");
}
return 0;
+
+finish:
+ memory_erase(packet, packet_length);
+ return r;
}
static int wall_tty_block(void) {
_cleanup_free_ char *p = NULL;
- int fd, r;
dev_t devnr;
+ int fd, r;
r = get_ctty_devnr(0, &devnr);
+ if (r == -ENXIO) /* We have no controlling tty */
+ return -ENOTTY;
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to get controlling TTY: %m");
if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(devnr), minor(devnr)) < 0)
- return -ENOMEM;
+ return log_oom();
mkdir_parents_label(p, 0700);
mkfifo(p, 0600);
fd = open(p, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
if (fd < 0)
- return -errno;
+ return log_debug_errno(errno, "Failed to open %s: %m", p);
return fd;
}
static bool wall_tty_match(const char *path, void *userdata) {
- int fd, r;
- struct stat st;
_cleanup_free_ char *p = NULL;
+ _cleanup_close_ int fd = -1;
+ struct stat st;
if (!path_is_absolute(path))
path = strjoina("/dev/", path);
- r = lstat(path, &st);
- if (r < 0)
+ if (lstat(path, &st) < 0) {
+ log_debug_errno(errno, "Failed to stat %s: %m", path);
return true;
+ }
- if (!S_ISCHR(st.st_mode))
+ if (!S_ISCHR(st.st_mode)) {
+ log_debug("%s is not a character device.", path);
return true;
+ }
/* We use named pipes to ensure that wall messages suggesting
* password entry are not printed over password prompts
@@ -404,16 +450,19 @@ static bool wall_tty_match(const char *path, void *userdata) {
* advantage that the block will automatically go away if the
* process dies. */
- if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0)
+ if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0) {
+ log_oom();
return true;
+ }
fd = open(p, O_WRONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
- if (fd < 0)
- return true;
+ if (fd < 0) {
+ log_debug_errno(errno, "Failed top open the wall pipe: %m");
+ return 1;
+ }
/* What, we managed to open the pipe? Then this tty is filtered. */
- safe_close(fd);
- return false;
+ return 0;
}
static int show_passwords(void) {
@@ -426,11 +475,10 @@ static int show_passwords(void) {
if (errno == ENOENT)
return 0;
- log_error_errno(errno, "opendir(/run/systemd/ask-password): %m");
- return -errno;
+ return log_error_errno(errno, "Failed to open /run/systemd/ask-password: %m");
}
- while ((de = readdir(d))) {
+ FOREACH_DIRENT_ALL(de, d, return log_error_errno(errno, "Failed to read directory: %m")) {
_cleanup_free_ char *p = NULL, *wall = NULL;
int q;
@@ -455,7 +503,7 @@ static int show_passwords(void) {
r = q;
if (wall)
- utmp_wall(wall, NULL, NULL, wall_tty_match, NULL);
+ (void) utmp_wall(wall, NULL, NULL, wall_tty_match, NULL);
}
return r;
@@ -475,14 +523,14 @@ static int watch_passwords(void) {
tty_block_fd = wall_tty_block();
- mkdir_p_label("/run/systemd/ask-password", 0755);
+ (void) mkdir_p_label("/run/systemd/ask-password", 0755);
notify = inotify_init1(IN_CLOEXEC);
if (notify < 0)
- return -errno;
+ return log_error_errno(errno, "Failed to allocate directory watch: %m");
if (inotify_add_watch(notify, "/run/systemd/ask-password", IN_CLOSE_WRITE|IN_MOVED_TO) < 0)
- return -errno;
+ return log_error_errno(errno, "Failed to add /run/systemd/ask-password to directory watch: %m");
assert_se(sigemptyset(&mask) >= 0);
assert_se(sigset_add_many(&mask, SIGINT, SIGTERM, -1) >= 0);
@@ -490,7 +538,7 @@ static int watch_passwords(void) {
signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
if (signal_fd < 0)
- return -errno;
+ return log_error_errno(errno, "Failed to allocate signal file descriptor: %m");
pollfd[FD_INOTIFY].fd = notify;
pollfd[FD_INOTIFY].events = POLLIN;
@@ -510,7 +558,7 @@ static int watch_passwords(void) {
}
if (pollfd[FD_INOTIFY].revents != 0)
- flush_fd(notify);
+ (void) flush_fd(notify);
if (pollfd[FD_SIGNAL].revents != 0)
break;
@@ -571,9 +619,7 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case ARG_LIST:
arg_action = ACTION_LIST;
@@ -628,8 +674,8 @@ int main(int argc, char *argv[]) {
goto finish;
if (arg_console) {
- setsid();
- release_terminal();
+ (void) setsid();
+ (void) release_terminal();
}
if (IN_SET(arg_action, ACTION_WATCH, ACTION_WALL))
@@ -637,9 +683,6 @@ int main(int argc, char *argv[]) {
else
r = show_passwords();
- if (r < 0)
- log_error_errno(r, "Error: %m");
-
finish:
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/udev/.gitignore b/src/udev/.gitignore
index ba112ce218..f5d8be3dc1 100644
--- a/src/udev/.gitignore
+++ b/src/udev/.gitignore
@@ -1,5 +1,4 @@
/udev.pc
/keyboard-keys-from-name.gperf
/keyboard-keys-from-name.h
-/keyboard-keys-to-name.h
/keyboard-keys-list.txt
diff --git a/src/udev/ata_id/ata_id.c b/src/udev/ata_id/ata_id.c
index 1d1798dd10..1e414664ce 100644
--- a/src/udev/ata_id/ata_id.c
+++ b/src/udev/ata_id/ata_id.c
@@ -19,28 +19,30 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <unistd.h>
-#include <fcntl.h>
#include <ctype.h>
-#include <string.h>
#include <errno.h>
+#include <fcntl.h>
#include <getopt.h>
+#include <linux/bsg.h>
+#include <linux/hdreg.h>
#include <scsi/scsi.h>
-#include <scsi/sg.h>
#include <scsi/scsi_ioctl.h>
+#include <scsi/sg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <sys/ioctl.h>
-#include <sys/types.h>
#include <sys/stat.h>
-#include <linux/hdreg.h>
-#include <linux/bsg.h>
+#include <sys/types.h>
+#include <unistd.h>
#include "libudev.h"
+
+#include "fd-util.h"
#include "libudev-private.h"
-#include "udev-util.h"
#include "log.h"
+#include "udev-util.h"
#define COMMAND_TIMEOUT_MSEC (30 * 1000)
diff --git a/src/udev/cdrom_id/cdrom_id.c b/src/udev/cdrom_id/cdrom_id.c
index 001bae7a24..72f284f710 100644
--- a/src/udev/cdrom_id/cdrom_id.c
+++ b/src/udev/cdrom_id/cdrom_id.c
@@ -17,24 +17,25 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <stdio.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <limits.h>
-#include <fcntl.h>
#include <errno.h>
+#include <fcntl.h>
#include <getopt.h>
-#include <time.h>
+#include <limits.h>
+#include <linux/cdrom.h>
#include <scsi/sg.h>
-#include <sys/types.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/time.h>
-#include <sys/ioctl.h>
-#include <linux/cdrom.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
#include "libudev.h"
+
#include "libudev-private.h"
#include "random-util.h"
diff --git a/src/udev/collect/collect.c b/src/udev/collect/collect.c
index b3a1f0bca1..349585b634 100644
--- a/src/udev/collect/collect.c
+++ b/src/udev/collect/collect.c
@@ -19,13 +19,16 @@
*
*/
-#include <stdio.h>
-#include <stddef.h>
#include <errno.h>
#include <getopt.h>
+#include <stddef.h>
+#include <stdio.h>
+#include "alloc-util.h"
#include "libudev-private.h"
#include "macro.h"
+#include "stdio-util.h"
+#include "string-util.h"
#define BUFSIZE 16
#define UDEV_ALARM_TIMEOUT 180
@@ -89,7 +92,7 @@ static int prepare(char *dir, char *filename)
if (r < 0 && errno != EEXIST)
return -errno;
- snprintf(buf, sizeof(buf), "%s/%s", dir, filename);
+ xsprintf(buf, "%s/%s", dir, filename);
fd = open(buf,O_RDWR|O_CREAT|O_CLOEXEC, S_IRUSR|S_IWUSR);
if (fd < 0)
diff --git a/src/udev/mtd_probe/mtd_probe.c b/src/udev/mtd_probe/mtd_probe.c
index 67b750c4b3..462fab7623 100644
--- a/src/udev/mtd_probe/mtd_probe.c
+++ b/src/udev/mtd_probe/mtd_probe.c
@@ -17,14 +17,14 @@
* Boston, MA 02110-1301 USA
*/
+#include <fcntl.h>
+#include <mtd/mtd-user.h>
#include <stdio.h>
+#include <stdlib.h>
#include <sys/ioctl.h>
-#include <mtd/mtd-user.h>
-#include <sys/types.h>
#include <sys/stat.h>
-#include <fcntl.h>
+#include <sys/types.h>
#include <unistd.h>
-#include <stdlib.h>
#include "mtd_probe.h"
diff --git a/src/udev/mtd_probe/probe_smartmedia.c b/src/udev/mtd_probe/probe_smartmedia.c
index a007ccee2f..6a6c5522a7 100644
--- a/src/udev/mtd_probe/probe_smartmedia.c
+++ b/src/udev/mtd_probe/probe_smartmedia.c
@@ -17,15 +17,16 @@
* Boston, MA 02110-1301 USA
*/
+#include <fcntl.h>
+#include <mtd/mtd-user.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
-#include <mtd/mtd-user.h>
#include <string.h>
-#include <sys/types.h>
#include <sys/stat.h>
-#include <fcntl.h>
+#include <sys/types.h>
#include <unistd.h>
-#include <stdint.h>
+
#include "mtd_probe.h"
static const uint8_t cis_signature[] = {
diff --git a/src/udev/net/ethtool-util.c b/src/udev/net/ethtool-util.c
index a4b05d1bec..0647008d90 100644
--- a/src/udev/net/ethtool-util.c
+++ b/src/udev/net/ethtool-util.c
@@ -19,17 +19,17 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/ioctl.h>
#include <net/if.h>
+#include <sys/ioctl.h>
#include <linux/ethtool.h>
#include <linux/sockios.h>
+#include "conf-parser.h"
#include "ethtool-util.h"
-
+#include "log.h"
+#include "string-table.h"
#include "strxcpyx.h"
#include "util.h"
-#include "log.h"
-#include "conf-parser.h"
static const char* const duplex_table[_DUP_MAX] = {
[DUP_FULL] = "full",
diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c
index 63e54db56e..77d9bf995a 100644
--- a/src/udev/net/link-config.c
+++ b/src/udev/net/link-config.c
@@ -22,22 +22,28 @@
#include <netinet/ether.h>
#include <linux/netdevice.h>
+#include "sd-netlink.h"
-#include "missing.h"
-#include "link-config.h"
+#include "alloc-util.h"
+#include "conf-files.h"
+#include "conf-parser.h"
#include "ethtool-util.h"
-
+#include "fd-util.h"
#include "libudev-private.h"
-#include "sd-netlink.h"
-#include "util.h"
+#include "link-config.h"
#include "log.h"
-#include "strv.h"
-#include "path-util.h"
-#include "conf-parser.h"
-#include "conf-files.h"
+#include "missing.h"
#include "netlink-util.h"
#include "network-internal.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "proc-cmdline.h"
#include "random-util.h"
+#include "stat-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "strv.h"
+#include "util.h"
struct link_config_ctx {
LIST_HEAD(link_config, links);
@@ -348,14 +354,14 @@ static int get_mac(struct udev_device *device, bool want_random,
if (want_random)
random_bytes(mac->ether_addr_octet, ETH_ALEN);
else {
- uint8_t result[8];
+ uint64_t result;
- r = net_get_unique_predictable_data(device, result);
+ r = net_get_unique_predictable_data(device, &result);
if (r < 0)
return r;
assert_cc(ETH_ALEN <= sizeof(result));
- memcpy(mac->ether_addr_octet, result, ETH_ALEN);
+ memcpy(mac->ether_addr_octet, &result, ETH_ALEN);
}
/* see eth_random_addr in the kernel */
@@ -460,6 +466,7 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
mac = &generated_mac;
}
break;
+ case MACPOLICY_NONE:
default:
mac = config->mac;
}
@@ -492,7 +499,8 @@ int link_get_driver(link_config_ctx *ctx, struct udev_device *device, char **ret
static const char* const mac_policy_table[_MACPOLICY_MAX] = {
[MACPOLICY_PERSISTENT] = "persistent",
- [MACPOLICY_RANDOM] = "random"
+ [MACPOLICY_RANDOM] = "random",
+ [MACPOLICY_NONE] = "none"
};
DEFINE_STRING_TABLE_LOOKUP(mac_policy, MACPolicy);
diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h
index 9875057e84..4fcbee8b92 100644
--- a/src/udev/net/link-config.h
+++ b/src/udev/net/link-config.h
@@ -21,10 +21,11 @@
#pragma once
-#include "ethtool-util.h"
+#include "libudev.h"
+
#include "condition.h"
+#include "ethtool-util.h"
#include "list.h"
-#include "libudev.h"
typedef struct link_config_ctx link_config_ctx;
typedef struct link_config link_config;
@@ -32,6 +33,7 @@ typedef struct link_config link_config;
typedef enum MACPolicy {
MACPOLICY_PERSISTENT,
MACPOLICY_RANDOM,
+ MACPOLICY_NONE,
_MACPOLICY_MAX,
_MACPOLICY_INVALID = -1
} MACPolicy;
diff --git a/src/udev/scsi_id/scsi.h b/src/udev/scsi_id/scsi.h
index 3f99ae7724..1054551d0b 100644
--- a/src/udev/scsi_id/scsi.h
+++ b/src/udev/scsi_id/scsi.h
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/*
* scsi.h
*
@@ -24,25 +26,25 @@ struct scsi_ioctl_command {
/*
* Default 5 second timeout
*/
-#define DEF_TIMEOUT 5000
+#define DEF_TIMEOUT 5000
-#define SENSE_BUFF_LEN 32
+#define SENSE_BUFF_LEN 32
/*
* The request buffer size passed to the SCSI INQUIRY commands, use 254,
* as this is a nice value for some devices, especially some of the usb
* mass storage devices.
*/
-#define SCSI_INQ_BUFF_LEN 254
+#define SCSI_INQ_BUFF_LEN 254
/*
* SCSI INQUIRY vendor and model (really product) lengths.
*/
-#define VENDOR_LENGTH 8
-#define MODEL_LENGTH 16
+#define VENDOR_LENGTH 8
+#define MODEL_LENGTH 16
-#define INQUIRY_CMD 0x12
-#define INQUIRY_CMDLEN 6
+#define INQUIRY_CMD 0x12
+#define INQUIRY_CMDLEN 6
/*
* INQUIRY VPD page 0x83 identifier descriptor related values. Reference the
@@ -52,34 +54,34 @@ struct scsi_ioctl_command {
/*
* id type values of id descriptors. These are assumed to fit in 4 bits.
*/
-#define SCSI_ID_VENDOR_SPECIFIC 0
-#define SCSI_ID_T10_VENDOR 1
-#define SCSI_ID_EUI_64 2
-#define SCSI_ID_NAA 3
-#define SCSI_ID_RELPORT 4
+#define SCSI_ID_VENDOR_SPECIFIC 0
+#define SCSI_ID_T10_VENDOR 1
+#define SCSI_ID_EUI_64 2
+#define SCSI_ID_NAA 3
+#define SCSI_ID_RELPORT 4
#define SCSI_ID_TGTGROUP 5
#define SCSI_ID_LUNGROUP 6
-#define SCSI_ID_MD5 7
-#define SCSI_ID_NAME 8
+#define SCSI_ID_MD5 7
+#define SCSI_ID_NAME 8
/*
* Supported NAA values. These fit in 4 bits, so the "don't care" value
* cannot conflict with real values.
*/
-#define SCSI_ID_NAA_DONT_CARE 0xff
-#define SCSI_ID_NAA_IEEE_REG 5
-#define SCSI_ID_NAA_IEEE_REG_EXTENDED 6
+#define SCSI_ID_NAA_DONT_CARE 0xff
+#define SCSI_ID_NAA_IEEE_REG 0x05
+#define SCSI_ID_NAA_IEEE_REG_EXTENDED 0x06
/*
* Supported Code Set values.
*/
-#define SCSI_ID_BINARY 1
-#define SCSI_ID_ASCII 2
+#define SCSI_ID_BINARY 1
+#define SCSI_ID_ASCII 2
struct scsi_id_search_values {
- u_char id_type;
- u_char naa_type;
- u_char code_set;
+ u_char id_type;
+ u_char naa_type;
+ u_char code_set;
};
/*
@@ -87,13 +89,13 @@ struct scsi_id_search_values {
* used a 1 bit right and masked version of these. So now CHECK_CONDITION
* and friends (in <scsi/scsi.h>) are deprecated.
*/
-#define SCSI_CHECK_CONDITION 0x2
-#define SCSI_CONDITION_MET 0x4
-#define SCSI_BUSY 0x8
-#define SCSI_IMMEDIATE 0x10
+#define SCSI_CHECK_CONDITION 0x02
+#define SCSI_CONDITION_MET 0x04
+#define SCSI_BUSY 0x08
+#define SCSI_IMMEDIATE 0x10
#define SCSI_IMMEDIATE_CONDITION_MET 0x14
-#define SCSI_RESERVATION_CONFLICT 0x18
-#define SCSI_COMMAND_TERMINATED 0x22
-#define SCSI_TASK_SET_FULL 0x28
-#define SCSI_ACA_ACTIVE 0x30
-#define SCSI_TASK_ABORTED 0x40
+#define SCSI_RESERVATION_CONFLICT 0x18
+#define SCSI_COMMAND_TERMINATED 0x22
+#define SCSI_TASK_SET_FULL 0x28
+#define SCSI_ACA_ACTIVE 0x30
+#define SCSI_TASK_ABORTED 0x40
diff --git a/src/udev/scsi_id/scsi_id.c b/src/udev/scsi_id/scsi_id.c
index adb91869df..e9ab7dce59 100644
--- a/src/udev/scsi_id/scsi_id.c
+++ b/src/udev/scsi_id/scsi_id.c
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/*
* Copyright (C) IBM Corp. 2003
* Copyright (C) SUSE Linux Products GmbH, 2006
@@ -16,22 +18,25 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <stdio.h>
-#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <signal.h>
#include <stdarg.h>
#include <stdbool.h>
-#include <unistd.h>
-#include <signal.h>
-#include <fcntl.h>
-#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
-#include <ctype.h>
-#include <getopt.h>
#include <sys/stat.h>
+#include <unistd.h>
#include "libudev.h"
+
+#include "fd-util.h"
#include "libudev-private.h"
#include "scsi_id.h"
+#include "string-util.h"
#include "udev-util.h"
static const struct option options[] = {
diff --git a/src/udev/scsi_id/scsi_id.h b/src/udev/scsi_id/scsi_id.h
index 141b116a88..25f3d1a3b7 100644
--- a/src/udev/scsi_id/scsi_id.h
+++ b/src/udev/scsi_id/scsi_id.h
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/*
* Copyright (C) IBM Corp. 2003
*
diff --git a/src/udev/scsi_id/scsi_serial.c b/src/udev/scsi_id/scsi_serial.c
index de3b4f7581..bc18af05af 100644
--- a/src/udev/scsi_id/scsi_serial.c
+++ b/src/udev/scsi_id/scsi_serial.c
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/*
* Copyright (C) IBM Corp. 2003
*
@@ -17,27 +19,29 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <stdio.h>
#include <errno.h>
-#include <string.h>
#include <fcntl.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <time.h>
#include <inttypes.h>
+#include <linux/bsg.h>
+#include <linux/types.h>
#include <scsi/scsi.h>
#include <scsi/sg.h>
-#include <linux/types.h>
-#include <linux/bsg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
#include "libudev.h"
+
#include "libudev-private.h"
+#include "random-util.h"
#include "scsi.h"
#include "scsi_id.h"
-#include "random-util.h"
+#include "string-util.h"
/*
* A priority based list of id, naa, and binary/ascii for the identifier
@@ -48,11 +52,11 @@
* is normally one or some small number of descriptors.
*/
static const struct scsi_id_search_values id_search_list[] = {
- { SCSI_ID_TGTGROUP, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY },
- { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG_EXTENDED, SCSI_ID_BINARY },
- { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG_EXTENDED, SCSI_ID_ASCII },
- { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG, SCSI_ID_BINARY },
- { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG, SCSI_ID_ASCII },
+ { SCSI_ID_TGTGROUP, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY },
+ { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG_EXTENDED, SCSI_ID_BINARY },
+ { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG_EXTENDED, SCSI_ID_ASCII },
+ { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG, SCSI_ID_BINARY },
+ { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG, SCSI_ID_ASCII },
/*
* Devices already exist using NAA values that are now marked
* reserved. These should not conflict with other values, or it is
@@ -62,14 +66,14 @@ static const struct scsi_id_search_values id_search_list[] = {
* non-IEEE descriptors in a random order will get different
* names.
*/
- { SCSI_ID_NAA, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY },
- { SCSI_ID_NAA, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII },
- { SCSI_ID_EUI_64, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY },
- { SCSI_ID_EUI_64, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII },
- { SCSI_ID_T10_VENDOR, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY },
- { SCSI_ID_T10_VENDOR, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII },
- { SCSI_ID_VENDOR_SPECIFIC, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY },
- { SCSI_ID_VENDOR_SPECIFIC, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII },
+ { SCSI_ID_NAA, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY },
+ { SCSI_ID_NAA, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII },
+ { SCSI_ID_EUI_64, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY },
+ { SCSI_ID_EUI_64, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII },
+ { SCSI_ID_T10_VENDOR, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY },
+ { SCSI_ID_T10_VENDOR, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII },
+ { SCSI_ID_VENDOR_SPECIFIC, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY },
+ { SCSI_ID_VENDOR_SPECIFIC, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII },
};
static const char hex_str[]="0123456789abcdef";
@@ -79,21 +83,21 @@ static const char hex_str[]="0123456789abcdef";
* are used here.
*/
-#define DID_NO_CONNECT 0x01 /* Unable to connect before timeout */
-#define DID_BUS_BUSY 0x02 /* Bus remain busy until timeout */
-#define DID_TIME_OUT 0x03 /* Timed out for some other reason */
-#define DRIVER_TIMEOUT 0x06
-#define DRIVER_SENSE 0x08 /* Sense_buffer has been set */
+#define DID_NO_CONNECT 0x01 /* Unable to connect before timeout */
+#define DID_BUS_BUSY 0x02 /* Bus remain busy until timeout */
+#define DID_TIME_OUT 0x03 /* Timed out for some other reason */
+#define DRIVER_TIMEOUT 0x06
+#define DRIVER_SENSE 0x08 /* Sense_buffer has been set */
/* The following "category" function returns one of the following */
#define SG_ERR_CAT_CLEAN 0 /* No errors or other information */
#define SG_ERR_CAT_MEDIA_CHANGED 1 /* interpreted from sense buffer */
#define SG_ERR_CAT_RESET 2 /* interpreted from sense buffer */
-#define SG_ERR_CAT_TIMEOUT 3
-#define SG_ERR_CAT_RECOVERED 4 /* Successful command after recovered err */
-#define SG_ERR_CAT_NOTSUPPORTED 5 /* Illegal / unsupported command */
-#define SG_ERR_CAT_SENSE 98 /* Something else in the sense buffer */
-#define SG_ERR_CAT_OTHER 99 /* Some other error/warning */
+#define SG_ERR_CAT_TIMEOUT 3
+#define SG_ERR_CAT_RECOVERED 4 /* Successful command after recovered err */
+#define SG_ERR_CAT_NOTSUPPORTED 5 /* Illegal / unsupported command */
+#define SG_ERR_CAT_SENSE 98 /* Something else in the sense buffer */
+#define SG_ERR_CAT_OTHER 99 /* Some other error/warning */
static int do_scsi_page80_inquiry(struct udev *udev,
struct scsi_id_device *dev_scsi, int fd,
@@ -210,7 +214,7 @@ static int scsi_dump_sense(struct udev *udev,
s = sense_buffer[7] + 8;
if (sb_len < s) {
log_debug("%s: sense buffer too small %d bytes, %d bytes too short",
- dev_scsi->kernel, sb_len, s - sb_len);
+ dev_scsi->kernel, sb_len, s - sb_len);
return -1;
}
if ((code == 0x0) || (code == 0x1)) {
@@ -220,7 +224,7 @@ static int scsi_dump_sense(struct udev *udev,
* Possible?
*/
log_debug("%s: sense result too" " small %d bytes",
- dev_scsi->kernel, s);
+ dev_scsi->kernel, s);
return -1;
}
asc = sense_buffer[12];
@@ -231,15 +235,15 @@ static int scsi_dump_sense(struct udev *udev,
ascq = sense_buffer[3];
} else {
log_debug("%s: invalid sense code 0x%x",
- dev_scsi->kernel, code);
+ dev_scsi->kernel, code);
return -1;
}
log_debug("%s: sense key 0x%x ASC 0x%x ASCQ 0x%x",
- dev_scsi->kernel, sense_key, asc, ascq);
+ dev_scsi->kernel, sense_key, asc, ascq);
} else {
if (sb_len < 4) {
log_debug("%s: sense buffer too small %d bytes, %d bytes too short",
- dev_scsi->kernel, sb_len, 4 - sb_len);
+ dev_scsi->kernel, sb_len, 4 - sb_len);
return -1;
}
@@ -247,9 +251,9 @@ static int scsi_dump_sense(struct udev *udev,
log_debug("%s: old sense key: 0x%x", dev_scsi->kernel, sense_buffer[0] & 0x0f);
else
log_debug("%s: sense = %2x %2x",
- dev_scsi->kernel, sense_buffer[0], sense_buffer[2]);
+ dev_scsi->kernel, sense_buffer[0], sense_buffer[2]);
log_debug("%s: non-extended sense class %d code 0x%0x",
- dev_scsi->kernel, sense_class, code);
+ dev_scsi->kernel, sense_class, code);
}
@@ -280,7 +284,7 @@ static int scsi_dump(struct udev *udev,
}
log_debug("%s: sg_io failed status 0x%x 0x%x 0x%x 0x%x",
- dev_scsi->kernel, io->driver_status, io->host_status, io->msg_status, io->status);
+ dev_scsi->kernel, io->driver_status, io->host_status, io->msg_status, io->status);
if (io->status == SCSI_CHECK_CONDITION)
return scsi_dump_sense(udev, dev_scsi, io->sbp, io->sb_len_wr);
else
@@ -300,8 +304,7 @@ static int scsi_dump_v4(struct udev *udev,
}
log_debug("%s: sg_io failed status 0x%x 0x%x 0x%x",
- dev_scsi->kernel, io->driver_status, io->transport_status,
- io->device_status);
+ dev_scsi->kernel, io->driver_status, io->transport_status, io->device_status);
if (io->device_status == SCSI_CHECK_CONDITION)
return scsi_dump_sense(udev, dev_scsi, (unsigned char *)(uintptr_t)io->response,
io->response_len);
@@ -397,7 +400,7 @@ resend:
error:
if (retval < 0)
log_debug("%s: Unable to get INQUIRY vpd %d page 0x%x.",
- dev_scsi->kernel, evpd, page);
+ dev_scsi->kernel, evpd, page);
return retval;
}
@@ -419,7 +422,7 @@ static int do_scsi_page0_inquiry(struct udev *udev,
return 1;
}
if (buffer[3] > len) {
- log_debug("%s: page 0 buffer too long %d", dev_scsi->kernel, buffer[3]);
+ log_debug("%s: page 0 buffer too long %d", dev_scsi->kernel, buffer[3]);
return 1;
}
@@ -462,7 +465,7 @@ static int prepend_vendor_model(struct udev *udev,
*/
if (ind != (VENDOR_LENGTH + MODEL_LENGTH)) {
log_debug("%s: expected length %d, got length %d",
- dev_scsi->kernel, (VENDOR_LENGTH + MODEL_LENGTH), ind);
+ dev_scsi->kernel, (VENDOR_LENGTH + MODEL_LENGTH), ind);
return -1;
}
return ind;
@@ -527,7 +530,7 @@ static int check_fill_0x83_id(struct udev *udev,
if (max_len < len) {
log_debug("%s: length %d too short - need %d",
- dev_scsi->kernel, max_len, len);
+ dev_scsi->kernel, max_len, len);
return 1;
}
@@ -783,7 +786,7 @@ static int do_scsi_page80_inquiry(struct udev *udev,
len = 1 + VENDOR_LENGTH + MODEL_LENGTH + buf[3];
if (max_len < len) {
log_debug("%s: length %d too short - need %d",
- dev_scsi->kernel, max_len, len);
+ dev_scsi->kernel, max_len, len);
return 1;
}
/*
diff --git a/src/udev/udev-builtin-blkid.c b/src/udev/udev-builtin-blkid.c
index b8066ea6e9..018b4dc596 100644
--- a/src/udev/udev-builtin-blkid.c
+++ b/src/udev/udev-builtin-blkid.c
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/*
* probe disks for filesystems and partitions
*
@@ -18,18 +20,22 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include <blkid/blkid.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <sys/stat.h>
-#include <blkid/blkid.h>
#include "sd-id128.h"
-#include "gpt.h"
+
+#include "alloc-util.h"
#include "efivars.h"
+#include "fd-util.h"
+#include "gpt.h"
+#include "string-util.h"
#include "udev.h"
static void print_property(struct udev_device *dev, bool test, const char *name, const char *value) {
@@ -118,7 +124,7 @@ static int find_gpt_root(struct udev_device *dev, blkid_probe pr, bool test) {
errno = 0;
pl = blkid_probe_get_partitions(pr);
if (!pl)
- return errno ? -errno : -ENOMEM;
+ return errno > 0 ? -errno : -ENOMEM;
nvals = blkid_partlist_numof_partitions(pl);
for (i = 0; i < nvals; i++) {
diff --git a/src/udev/udev-builtin-btrfs.c b/src/udev/udev-builtin-btrfs.c
index 3352821567..467010f5b3 100644
--- a/src/udev/udev-builtin-btrfs.c
+++ b/src/udev/udev-builtin-btrfs.c
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/***
This file is part of systemd.
@@ -17,15 +19,17 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
#include <fcntl.h>
+#include <stdlib.h>
#include <sys/ioctl.h>
#ifdef HAVE_LINUX_BTRFS_H
#include <linux/btrfs.h>
#endif
+#include "fd-util.h"
#include "missing.h"
+#include "string-util.h"
#include "udev.h"
static int builtin_btrfs(struct udev_device *dev, int argc, char *argv[], bool test) {
diff --git a/src/udev/udev-builtin-hwdb.c b/src/udev/udev-builtin-hwdb.c
index 72109d93d2..a9e312e2c0 100644
--- a/src/udev/udev-builtin-hwdb.c
+++ b/src/udev/udev-builtin-hwdb.c
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/***
This file is part of systemd.
@@ -17,16 +19,18 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
-#include <stdlib.h>
#include <fnmatch.h>
#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
-#include "udev.h"
#include "sd-hwdb.h"
+#include "alloc-util.h"
#include "hwdb-util.h"
+#include "string-util.h"
#include "udev-util.h"
+#include "udev.h"
static sd_hwdb *hwdb;
diff --git a/src/udev/udev-builtin-input_id.c b/src/udev/udev-builtin-input_id.c
index e3fa4bc162..691ef5656d 100644
--- a/src/udev/udev-builtin-input_id.c
+++ b/src/udev/udev-builtin-input_id.c
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/*
* expose input properties via udev
*
@@ -21,15 +23,18 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <errno.h>
+#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
-#include <stdarg.h>
-#include <unistd.h>
#include <string.h>
-#include <errno.h>
+#include <unistd.h>
#include <linux/limits.h>
#include <linux/input.h>
+#include "fd-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
#include "udev.h"
#include "util.h"
@@ -62,8 +67,8 @@ static void extract_info(struct udev_device *dev, const char *devpath, bool test
if (xabsinfo.resolution <= 0 || yabsinfo.resolution <= 0)
return;
- snprintf(width, sizeof(width), "%d", abs_size_mm(&xabsinfo));
- snprintf(height, sizeof(height), "%d", abs_size_mm(&yabsinfo));
+ xsprintf(width, "%d", abs_size_mm(&xabsinfo));
+ xsprintf(height, "%d", abs_size_mm(&yabsinfo));
udev_builtin_add_property(dev, test, "ID_INPUT_WIDTH_MM", width);
udev_builtin_add_property(dev, test, "ID_INPUT_HEIGHT_MM", height);
@@ -89,7 +94,7 @@ static void get_cap_mask(struct udev_device *dev,
if (!v)
v = "";
- snprintf(text, sizeof(text), "%s", v);
+ xsprintf(text, "%s", v);
log_debug("%s raw kernel attribute: %s", attr, text);
memzero(bitmask, bitmask_size);
@@ -111,7 +116,8 @@ static void get_cap_mask(struct udev_device *dev,
if (test) {
/* printf pattern with the right unsigned long number of hex chars */
- snprintf(text, sizeof(text), " bit %%4u: %%0%zulX\n", 2 * sizeof(unsigned long));
+ xsprintf(text, " bit %%4u: %%0%zulX\n",
+ 2 * sizeof(unsigned long));
log_debug("%s decoded bit map:", attr);
val = bitmask_size / sizeof (unsigned long);
/* skip over leading zeros */
@@ -201,12 +207,12 @@ static bool test_pointers(struct udev_device *dev,
/* This path is taken by VMware's USB mouse, which has
* absolute axes, but no touch/pressure button. */
is_mouse = true;
- else if (has_touch)
+ else if (has_touch || is_direct)
is_touchscreen = true;
else if (has_joystick_axes_or_buttons)
is_joystick = true;
}
- if (has_mt_coordinates && is_direct)
+ if (has_mt_coordinates && (is_direct || has_touch))
is_touchscreen = true;
if (has_rel_coordinates && has_mouse_button)
diff --git a/src/udev/udev-builtin-keyboard.c b/src/udev/udev-builtin-keyboard.c
index d63a8e2760..b80be52567 100644
--- a/src/udev/udev-builtin-keyboard.c
+++ b/src/udev/udev-builtin-keyboard.c
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/***
This file is part of systemd.
@@ -18,11 +20,15 @@
***/
#include <stdio.h>
-#include <string.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/ioctl.h>
#include <linux/input.h>
+#include "fd-util.h"
+#include "parse-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
#include "udev.h"
static const struct key *keyboard_lookup_key(const char *str, unsigned len);
diff --git a/src/udev/udev-builtin-kmod.c b/src/udev/udev-builtin-kmod.c
index 81e78a8aa3..9210d1cc71 100644
--- a/src/udev/udev-builtin-kmod.c
+++ b/src/udev/udev-builtin-kmod.c
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/*
* load kernel modules
*
@@ -18,12 +20,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
#include <errno.h>
#include <libkmod.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "string-util.h"
#include "udev.h"
static struct kmod_ctx *ctx = NULL;
diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c
index 589f1f7822..e83b8b1c12 100644
--- a/src/udev/udev-builtin-net_id.c
+++ b/src/udev/udev-builtin-net_id.c
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/***
This file is part of systemd.
@@ -27,7 +29,7 @@
* http://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames
*
* Two character prefixes based on the type of interface:
- * en -- ethernet
+ * en -- Ethernet
* sl -- serial line IP (slip)
* wl -- wlan
* ww -- wwan
@@ -53,17 +55,17 @@
* exported.
* The usual USB configuration == 1 and interface == 0 values are suppressed.
*
- * PCI ethernet card with firmware index "1":
+ * PCI Ethernet card with firmware index "1":
* ID_NET_NAME_ONBOARD=eno1
* ID_NET_NAME_ONBOARD_LABEL=Ethernet Port 1
*
- * PCI ethernet card in hotplug slot with firmware index number:
+ * PCI Ethernet card in hotplug slot with firmware index number:
* /sys/devices/pci0000:00/0000:00:1c.3/0000:05:00.0/net/ens1
* ID_NET_NAME_MAC=enx000000000466
* ID_NET_NAME_PATH=enp5s0
* ID_NET_NAME_SLOT=ens1
*
- * PCI ethernet multi-function card with 2 ports:
+ * PCI Ethernet multi-function card with 2 ports:
* /sys/devices/pci0000:00/0000:00:1c.0/0000:02:00.0/net/enp2s0f0
* ID_NET_NAME_MAC=enx78e7d1ea46da
* ID_NET_NAME_PATH=enp2s0f0
@@ -87,19 +89,22 @@
* ID_NET_NAME_PATH=enp0s29u1u2
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <string.h>
#include <errno.h>
+#include <fcntl.h>
#include <net/if.h>
#include <net/if_arp.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
#include <linux/pci_regs.h>
-#include "udev.h"
+#include "fd-util.h"
#include "fileio.h"
+#include "stdio-util.h"
+#include "string-util.h"
+#include "udev.h"
enum netname_type{
NET_UNDEF,
@@ -224,7 +229,7 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
err = -ENOENT;
goto out;
}
- snprintf(slots, sizeof(slots), "%s/slots", udev_device_get_syspath(pci));
+ xsprintf(slots, "%s/slots", udev_device_get_syspath(pci));
dir = opendir(slots);
if (!dir) {
err = -errno;
@@ -243,7 +248,7 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
continue;
if (i < 1)
continue;
- snprintf(str, sizeof(str), "%s/%s/address", slots, dent->d_name);
+ xsprintf(str, "%s/%s/address", slots, dent->d_name);
if (read_one_line_file(str, &address) >= 0) {
/* match slot address with device by stripping the function */
if (strneq(address, udev_device_get_sysname(names->pcidev), strlen(address)))
@@ -376,7 +381,7 @@ static int names_bcma(struct udev_device *dev, struct netnames *names) {
return -EINVAL;
/* suppress the common core == 0 */
if (core > 0)
- snprintf(names->bcma_core, sizeof(names->bcma_core), "b%u", core);
+ xsprintf(names->bcma_core, "b%u", core);
names->type = NET_BCMA;
return 0;
@@ -465,9 +470,9 @@ static int ieee_oui(struct udev_device *dev, struct netnames *names, bool test)
/* skip commonly misused 00:00:00 (Xerox) prefix */
if (memcmp(names->mac, "\0\0\0", 3) == 0)
return -EINVAL;
- snprintf(str, sizeof(str), "OUI:%02X%02X%02X%02X%02X%02X",
- names->mac[0], names->mac[1], names->mac[2],
- names->mac[3], names->mac[4], names->mac[5]);
+ xsprintf(str, "OUI:%02X%02X%02X%02X%02X%02X", names->mac[0],
+ names->mac[1], names->mac[2], names->mac[3], names->mac[4],
+ names->mac[5]);
udev_builtin_hwdb_lookup(dev, NULL, str, NULL, test);
return 0;
}
@@ -519,7 +524,7 @@ static int builtin_net_id(struct udev_device *dev, int argc, char *argv[], bool
if (err >= 0 && names.mac_valid) {
char str[IFNAMSIZ];
- snprintf(str, sizeof(str), "%sx%02x%02x%02x%02x%02x%02x", prefix,
+ xsprintf(str, "%sx%02x%02x%02x%02x%02x%02x", prefix,
names.mac[0], names.mac[1], names.mac[2],
names.mac[3], names.mac[4], names.mac[5]);
udev_builtin_add_property(dev, test, "ID_NET_NAME_MAC", str);
diff --git a/src/udev/udev-builtin-net_setup_link.c b/src/udev/udev-builtin-net_setup_link.c
index d4589470fb..f72894b5c5 100644
--- a/src/udev/udev-builtin-net_setup_link.c
+++ b/src/udev/udev-builtin-net_setup_link.c
@@ -19,9 +19,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "alloc-util.h"
#include "link-config.h"
-#include "udev.h"
#include "log.h"
+#include "udev.h"
static link_config_ctx *ctx = NULL;
diff --git a/src/udev/udev-builtin-path_id.c b/src/udev/udev-builtin-path_id.c
index 01e2c659ae..7851cec17f 100644
--- a/src/udev/udev-builtin-path_id.c
+++ b/src/udev/udev-builtin-path_id.c
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/*
* compose persistent device path
*
@@ -19,17 +21,19 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#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 <errno.h>
+#include <fcntl.h>
#include <getopt.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "alloc-util.h"
+#include "string-util.h"
#include "udev.h"
_printf_(2,3)
@@ -591,31 +595,23 @@ static struct udev_device *handle_bcma(struct udev_device *parent, char **path)
return parent;
}
-static struct udev_device *handle_ccw(struct udev_device *parent, struct udev_device *dev, char **path) {
- struct udev_device *scsi_dev;
+/* Handle devices of AP bus in System z platform. */
+static struct udev_device *handle_ap(struct udev_device *parent, char **path) {
+ const char *type, *func;
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;
- const char *lun;
- const char *hba_id;
-
- hba_id = udev_device_get_sysattr_value(scsi_dev, "hba_id");
- wwpn = udev_device_get_sysattr_value(scsi_dev, "wwpn");
- lun = udev_device_get_sysattr_value(scsi_dev, "fcp_lun");
- if (hba_id != NULL && lun != NULL && wwpn != NULL) {
- path_prepend(path, "ccw-%s-zfcp-%s:%s", hba_id, wwpn, lun);
- goto out;
- }
- }
+ type = udev_device_get_sysattr_value(parent, "type");
+ func = udev_device_get_sysattr_value(parent, "ap_functions");
- path_prepend(path, "ccw-%s", udev_device_get_sysname(parent));
+ if (type != NULL && func != NULL) {
+ path_prepend(path, "ap-%s-%s", type, func);
+ goto out;
+ }
+ path_prepend(path, "ap-%s", udev_device_get_sysname(parent));
out:
- parent = skip_subsystem(parent, "ccw");
+ parent = skip_subsystem(parent, "ap");
return parent;
}
@@ -627,13 +623,6 @@ static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool
assert(dev);
- /* S390 ccw bus */
- parent = udev_device_get_parent_with_subsystem_devtype(dev, "ccw", NULL);
- if (parent != NULL) {
- handle_ccw(parent, dev, &path);
- goto out;
- }
-
/* walk up the chain of devices and compose path */
parent = dev;
while (parent != NULL) {
@@ -681,6 +670,25 @@ static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool
parent = skip_subsystem(parent, "scm");
supported_transport = true;
supported_parent = true;
+ } else if (streq(subsys, "ccw")) {
+ path_prepend(&path, "ccw-%s", udev_device_get_sysname(parent));
+ parent = skip_subsystem(parent, "ccw");
+ supported_transport = true;
+ supported_parent = true;
+ } else if (streq(subsys, "ccwgroup")) {
+ path_prepend(&path, "ccwgroup-%s", udev_device_get_sysname(parent));
+ parent = skip_subsystem(parent, "ccwgroup");
+ supported_transport = true;
+ supported_parent = true;
+ } else if (streq(subsys, "ap")) {
+ parent = handle_ap(parent, &path);
+ supported_transport = true;
+ supported_parent = true;
+ } else if (streq(subsys, "iucv")) {
+ path_prepend(&path, "iucv-%s", udev_device_get_sysname(parent));
+ parent = skip_subsystem(parent, "iucv");
+ supported_transport = true;
+ supported_parent = true;
}
if (parent)
@@ -703,7 +711,6 @@ static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool
if (streq(udev_device_get_subsystem(dev), "block") && !supported_transport)
path = mfree(path);
-out:
if (path != NULL) {
char tag[UTIL_NAME_SIZE];
size_t i;
diff --git a/src/udev/udev-builtin-uaccess.c b/src/udev/udev-builtin-uaccess.c
index 7bf4e7f24d..b650a15bd8 100644
--- a/src/udev/udev-builtin-uaccess.c
+++ b/src/udev/udev-builtin-uaccess.c
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/*
* manage device node user ACL
*
@@ -18,11 +20,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
-#include <errno.h>
-#include "systemd/sd-login.h"
+#include "sd-login.h"
+
+#include "login-util.h"
#include "logind-acl.h"
#include "udev.h"
#include "util.h"
@@ -56,7 +60,7 @@ static int builtin_uaccess(struct udev_device *dev, int argc, char *argv[], bool
r = devnode_acl(path, true, false, 0, true, uid);
if (r < 0) {
- log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, r, "Failed to apply ACL on %s: %m", path);
+ log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_ERR, r, "Failed to apply ACL on %s: %m", path);
goto finish;
}
diff --git a/src/udev/udev-builtin-usb_id.c b/src/udev/udev-builtin-usb_id.c
index d309dc31cb..40d1e8cc47 100644
--- a/src/udev/udev-builtin-usb_id.c
+++ b/src/udev/udev-builtin-usb_id.c
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/*
* USB device properties and persistent device path
*
@@ -20,15 +22,18 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
#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 <unistd.h>
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "string-util.h"
#include "udev.h"
static void set_usb_iftype(char *to, int if_class_num, size_t len) {
diff --git a/src/udev/udev-builtin.c b/src/udev/udev-builtin.c
index 4f625251d6..18fb6615d5 100644
--- a/src/udev/udev-builtin.c
+++ b/src/udev/udev-builtin.c
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/***
This file is part of systemd.
@@ -17,10 +19,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <getopt.h>
#include <stdio.h>
#include <string.h>
-#include <getopt.h>
+#include "string-util.h"
#include "udev.h"
static bool initialized;
diff --git a/src/udev/udev-ctrl.c b/src/udev/udev-ctrl.c
index 56277f551f..10dd747256 100644
--- a/src/udev/udev-ctrl.c
+++ b/src/udev/udev-ctrl.c
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/*
* libudev - interface to udev device information
*
@@ -10,16 +12,18 @@
*/
#include <errno.h>
-#include <stdlib.h>
+#include <poll.h>
#include <stddef.h>
+#include <stdlib.h>
#include <string.h>
-#include <unistd.h>
-#include <poll.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include <unistd.h>
-#include "socket-util.h"
+#include "alloc-util.h"
+#include "fd-util.h"
#include "formats-util.h"
+#include "socket-util.h"
#include "udev.h"
/* wire protocol magic must match */
diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c
index 1e34cbc2f5..c1dcee6c73 100644
--- a/src/udev/udev-event.c
+++ b/src/udev/udev-event.c
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/*
* Copyright (C) 2003-2013 Kay Sievers <kay@vrfy.org>
*
@@ -15,26 +17,28 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <stdlib.h>
-#include <stdio.h>
-#include <stddef.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
#include <ctype.h>
-#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
#include <net/if.h>
-#include <sys/prctl.h>
#include <poll.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <sys/epoll.h>
-#include <sys/wait.h>
+#include <sys/prctl.h>
#include <sys/signalfd.h>
+#include <sys/wait.h>
+#include <unistd.h>
-#include "netlink-util.h"
-#include "event-util.h"
+#include "alloc-util.h"
+#include "fd-util.h"
#include "formats-util.h"
+#include "netlink-util.h"
#include "process-util.h"
#include "signal-util.h"
+#include "string-util.h"
#include "udev.h"
typedef struct Spawn {
@@ -438,9 +442,7 @@ static int spawn_exec(struct udev_event *event,
execve(argv[0], argv, envp);
/* exec failed */
- log_error_errno(errno, "failed to execute '%s' '%s': %m", argv[0], cmd);
-
- return -errno;
+ return log_error_errno(errno, "failed to execute '%s' '%s': %m", argv[0], cmd);
}
static void spawn_read(struct udev_event *event,
@@ -637,7 +639,7 @@ static int spawn_wait(struct udev_event *event,
.pid = pid,
.accept_failure = accept_failure,
};
- _cleanup_event_unref_ sd_event *e = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
int r, ret;
r = sd_event_new(&e);
@@ -848,11 +850,11 @@ void udev_event_execute_rules(struct udev_event *event,
/* 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"))
- udev_device_copy_properties(dev, event->dev_db);
+ if (major(udev_device_get_devnum(dev)) == 0 &&
+ 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,
diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c
index e730fb45f1..fd7936c2dc 100644
--- a/src/udev/udev-node.c
+++ b/src/udev/udev-node.c
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/*
* Copyright (C) 2003-2013 Kay Sievers <kay@vrfy.org>
*
@@ -15,20 +17,23 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <string.h>
-#include <stdio.h>
-#include <stddef.h>
-#include <stdbool.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <errno.h>
#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
#include <sys/stat.h>
+#include <unistd.h>
-#include "udev.h"
-#include "smack-util.h"
-#include "selinux-util.h"
#include "formats-util.h"
+#include "fs-util.h"
+#include "selinux-util.h"
+#include "smack-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
+#include "udev.h"
static int node_symlink(struct udev_device *dev, const char *node, const char *slink) {
struct stat stats;
@@ -261,8 +266,7 @@ static int node_permissions_apply(struct udev_device *dev, bool apply,
mode |= S_IFCHR;
if (lstat(devnode, &stats) != 0) {
- err = -errno;
- log_debug_errno(errno, "can not stat() node '%s' (%m)", devnode);
+ err = log_debug_errno(errno, "can not stat() node '%s' (%m)", devnode);
goto out;
}
@@ -345,9 +349,10 @@ void udev_node_add(struct udev_device *dev, bool apply,
return;
/* always add /dev/{block,char}/$major:$minor */
- snprintf(filename, sizeof(filename), "/dev/%s/%u:%u",
+ xsprintf(filename, "/dev/%s/%u:%u",
streq(udev_device_get_subsystem(dev), "block") ? "block" : "char",
- major(udev_device_get_devnum(dev)), minor(udev_device_get_devnum(dev)));
+ major(udev_device_get_devnum(dev)),
+ minor(udev_device_get_devnum(dev)));
node_symlink(dev, udev_device_get_devnode(dev), filename);
/* create/update symlinks, add symlinks to name index */
@@ -364,8 +369,9 @@ void udev_node_remove(struct udev_device *dev) {
link_update(dev, udev_list_entry_get_name(list_entry), false);
/* remove /dev/{block,char}/$major:$minor */
- snprintf(filename, sizeof(filename), "/dev/%s/%u:%u",
+ xsprintf(filename, "/dev/%s/%u:%u",
streq(udev_device_get_subsystem(dev), "block") ? "block" : "char",
- major(udev_device_get_devnum(dev)), minor(udev_device_get_devnum(dev)));
+ major(udev_device_get_devnum(dev)),
+ minor(udev_device_get_devnum(dev)));
unlink(filename);
}
diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c
index 98c33171d4..7342f2849e 100644
--- a/src/udev/udev-rules.c
+++ b/src/udev/udev-rules.c
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/*
* Copyright (C) 2003-2012 Kay Sievers <kay@vrfy.org>
*
@@ -15,27 +17,34 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <stddef.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <string.h>
-#include <stdio.h>
-#include <fcntl.h>
#include <ctype.h>
-#include <unistd.h>
-#include <errno.h>
#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
#include <fnmatch.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <time.h>
+#include <unistd.h>
-#include "udev.h"
-#include "path-util.h"
+#include "alloc-util.h"
#include "conf-files.h"
+#include "escape.h"
+#include "fd-util.h"
+#include "glob-util.h"
+#include "path-util.h"
+#include "stat-util.h"
#include "strbuf.h"
+#include "string-util.h"
#include "strv.h"
-#include "util.h"
#include "sysctl-util.h"
+#include "udev.h"
+#include "user-util.h"
+#include "util.h"
#define PREALLOC_TOKEN 2048
@@ -51,7 +60,8 @@ static const char* const rules_dirs[] = {
"/etc/udev/rules.d",
"/run/udev/rules.d",
UDEVLIBEXECDIR "/rules.d",
- NULL};
+ NULL
+};
struct udev_rules {
struct udev *udev;
@@ -1937,7 +1947,8 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
break;
}
}
- if (!match && (cur->key.op != OP_NOMATCH))
+ if ((!match && (cur->key.op != OP_NOMATCH)) ||
+ (match && (cur->key.op == OP_NOMATCH)))
goto nomatch;
break;
}
@@ -2514,7 +2525,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
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));
+ log_error_errno(r, "error writing SYSCTL{%s}='%s': %m", filename, value);
break;
}
case TK_A_RUN_BUILTIN:
diff --git a/src/udev/udev-watch.c b/src/udev/udev-watch.c
index 9aa5ab185d..c0f4973f93 100644
--- a/src/udev/udev-watch.c
+++ b/src/udev/udev-watch.c
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/*
* Copyright (C) 2004-2012 Kay Sievers <kay@vrfy.org>
* Copyright (C) 2009 Canonical Ltd.
@@ -17,13 +19,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <errno.h>
-#include <stdio.h>
#include <dirent.h>
+#include <errno.h>
#include <stddef.h>
-#include <unistd.h>
+#include <stdio.h>
#include <sys/inotify.h>
+#include <unistd.h>
+#include "stdio-util.h"
#include "udev.h"
static int inotify_fd = -1;
@@ -103,7 +106,7 @@ void udev_watch_begin(struct udev *udev, struct udev_device *dev) {
return;
}
- snprintf(filename, sizeof(filename), "/run/udev/watch/%d", wd);
+ xsprintf(filename, "/run/udev/watch/%d", wd);
mkdir_parents(filename, 0755);
unlink(filename);
r = symlink(udev_device_get_id_filename(dev), filename);
@@ -127,7 +130,7 @@ void udev_watch_end(struct udev *udev, struct udev_device *dev) {
log_debug("removing watch on '%s'", udev_device_get_devnode(dev));
inotify_rm_watch(inotify_fd, wd);
- snprintf(filename, sizeof(filename), "/run/udev/watch/%d", wd);
+ xsprintf(filename, "/run/udev/watch/%d", wd);
unlink(filename);
udev_device_set_watch_handle(dev, -1);
@@ -141,7 +144,7 @@ struct udev_device *udev_watch_lookup(struct udev *udev, int wd) {
if (inotify_fd < 0 || wd < 0)
return NULL;
- snprintf(filename, sizeof(filename), "/run/udev/watch/%d", wd);
+ xsprintf(filename, "/run/udev/watch/%d", wd);
len = readlink(filename, device, sizeof(device));
if (len <= 0 || (size_t)len == sizeof(device))
return NULL;
diff --git a/src/udev/udev.h b/src/udev/udev.h
index d17fc8c1ea..4f4002056c 100644
--- a/src/udev/udev.h
+++ b/src/udev/udev.h
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/*
* Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
* Copyright (C) 2003-2010 Kay Sievers <kay@vrfy.org>
@@ -18,16 +20,17 @@
#pragma once
-#include <sys/types.h>
#include <sys/param.h>
+#include <sys/types.h>
-#include "macro.h"
-#include "sd-netlink.h"
#include "libudev.h"
-#include "libudev-private.h"
-#include "util.h"
+#include "sd-netlink.h"
+
#include "label.h"
+#include "libudev-private.h"
+#include "macro.h"
#include "strv.h"
+#include "util.h"
struct udev_event {
struct udev *udev;
diff --git a/src/udev/udevadm-control.c b/src/udev/udevadm-control.c
index 78170463b6..119033c2af 100644
--- a/src/udev/udevadm-control.c
+++ b/src/udev/udevadm-control.c
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/*
* Copyright (C) 2005-2011 Kay Sievers <kay@vrfy.org>
*
@@ -13,15 +15,15 @@
*/
#include <errno.h>
+#include <getopt.h>
+#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
-#include <stddef.h>
#include <string.h>
#include <unistd.h>
-#include <getopt.h>
-#include "udev.h"
#include "udev-util.h"
+#include "udev.h"
static void print_help(void) {
printf("%s control COMMAND\n\n"
diff --git a/src/udev/udevadm-hwdb.c b/src/udev/udevadm-hwdb.c
index 00609e31b5..53f0871957 100644
--- a/src/udev/udevadm-hwdb.c
+++ b/src/udev/udevadm-hwdb.c
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/***
This file is part of systemd.
@@ -17,18 +19,21 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
+#include <ctype.h>
#include <getopt.h>
+#include <stdlib.h>
#include <string.h>
-#include <ctype.h>
-#include "util.h"
-#include "strbuf.h"
+#include "alloc-util.h"
#include "conf-files.h"
-
-#include "udev.h"
+#include "fileio.h"
+#include "fs-util.h"
#include "hwdb-internal.h"
#include "hwdb-util.h"
+#include "strbuf.h"
+#include "string-util.h"
+#include "udev.h"
+#include "util.h"
/*
* Generic udev properties, key/value database based on modalias strings.
@@ -662,7 +667,7 @@ static int adm_hwdb(struct udev *udev, int argc, char *argv[]) {
}
if (test) {
- _cleanup_hwdb_unref_ sd_hwdb *hwdb = NULL;
+ _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
int r;
r = sd_hwdb_new(&hwdb);
diff --git a/src/udev/udevadm-info.c b/src/udev/udevadm-info.c
index b3d5565c48..ca67c385b4 100644
--- a/src/udev/udevadm-info.c
+++ b/src/udev/udevadm-info.c
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/*
* Copyright (C) 2004-2009 Kay Sievers <kay@vrfy.org>
*
@@ -15,19 +17,21 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <string.h>
-#include <stdio.h>
-#include <stddef.h>
#include <ctype.h>
-#include <unistd.h>
#include <dirent.h>
#include <errno.h>
-#include <getopt.h>
#include <fcntl.h>
+#include <getopt.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
#include <sys/stat.h>
+#include <unistd.h>
-#include "udev.h"
+#include "fd-util.h"
+#include "string-util.h"
#include "udev-util.h"
+#include "udev.h"
#include "udevadm-util.h"
static bool skip_attribute(const char *name) {
diff --git a/src/udev/udevadm-monitor.c b/src/udev/udevadm-monitor.c
index 5e93955186..1579894082 100644
--- a/src/udev/udevadm-monitor.c
+++ b/src/udev/udevadm-monitor.c
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/*
* Copyright (C) 2004-2010 Kay Sievers <kay@vrfy.org>
*
@@ -15,19 +17,20 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <stdio.h>
-#include <stddef.h>
-#include <string.h>
#include <errno.h>
-#include <signal.h>
#include <getopt.h>
-#include <time.h>
-#include <sys/time.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
#include <sys/epoll.h>
+#include <sys/time.h>
+#include <time.h>
-#include "udev.h"
-#include "udev-util.h"
+#include "fd-util.h"
#include "formats-util.h"
+#include "udev-util.h"
+#include "udev.h"
static bool udev_exit;
diff --git a/src/udev/udevadm-settle.c b/src/udev/udevadm-settle.c
index 79f45610db..2cc9f123bd 100644
--- a/src/udev/udevadm-settle.c
+++ b/src/udev/udevadm-settle.c
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/*
* Copyright (C) 2006-2009 Kay Sievers <kay@vrfy.org>
* Copyright (C) 2009 Canonical Ltd.
@@ -17,15 +19,16 @@
* 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 <errno.h>
#include <getopt.h>
#include <poll.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "parse-util.h"
#include "udev.h"
#include "util.h"
@@ -65,10 +68,9 @@ static int adm_settle(struct udev *udev, int argc, char *argv[]) {
r = safe_atou(optarg, &timeout);
if (r < 0) {
- fprintf(stderr, "Invalid timeout value '%s': %s\n",
- optarg, strerror(-r));
- exit(EXIT_FAILURE);
- };
+ log_error_errno(r, "Invalid timeout value '%s': %m", optarg);
+ return EXIT_FAILURE;
+ }
break;
}
diff --git a/src/udev/udevadm-test-builtin.c b/src/udev/udevadm-test-builtin.c
index 35a7349439..7389ca1b72 100644
--- a/src/udev/udevadm-test-builtin.c
+++ b/src/udev/udevadm-test-builtin.c
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/*
* Copyright (C) 2011 Kay Sievers <kay@vrfy.org>
*
@@ -15,12 +17,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <stdlib.h>
-#include <stddef.h>
-#include <stdio.h>
#include <errno.h>
#include <getopt.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "string-util.h"
#include "udev.h"
static void help(struct udev *udev) {
diff --git a/src/udev/udevadm-test.c b/src/udev/udevadm-test.c
index d04e618d0d..00ad917efc 100644
--- a/src/udev/udevadm-test.c
+++ b/src/udev/udevadm-test.c
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/*
* Copyright (C) 2003-2004 Greg Kroah-Hartman <greg@kroah.com>
* Copyright (C) 2004-2008 Kay Sievers <kay@vrfy.org>
@@ -16,17 +18,18 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <stdlib.h>
-#include <stdio.h>
-#include <stddef.h>
-#include <unistd.h>
#include <errno.h>
-#include <signal.h>
#include <getopt.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <sys/signalfd.h>
+#include <unistd.h>
-#include "udev.h"
+#include "string-util.h"
#include "udev-util.h"
+#include "udev.h"
static void help(void) {
diff --git a/src/udev/udevadm-trigger.c b/src/udev/udevadm-trigger.c
index 7af9665f8a..1385b87b3a 100644
--- a/src/udev/udevadm-trigger.c
+++ b/src/udev/udevadm-trigger.c
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/*
* Copyright (C) 2008-2009 Kay Sievers <kay@vrfy.org>
*
@@ -15,16 +17,17 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
#include <stddef.h>
-#include <string.h>
#include <stdio.h>
+#include <string.h>
#include <unistd.h>
-#include <getopt.h>
-#include <errno.h>
-#include <fcntl.h>
-#include "udev.h"
+#include "string-util.h"
#include "udev-util.h"
+#include "udev.h"
#include "udevadm-util.h"
#include "util.h"
diff --git a/src/udev/udevadm-util.c b/src/udev/udevadm-util.c
index 3f0e45e26c..94cbe21f3e 100644
--- a/src/udev/udevadm-util.c
+++ b/src/udev/udevadm-util.c
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/*
* Copyright (C) 2008-2009 Kay Sievers <kay@vrfy.org>
*
@@ -15,6 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include "string-util.h"
#include "udevadm-util.h"
struct udev_device *find_device(struct udev *udev,
diff --git a/src/udev/udevadm-util.h b/src/udev/udevadm-util.h
index 37e4fe8369..5882096081 100644
--- a/src/udev/udevadm-util.h
+++ b/src/udev/udevadm-util.h
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/*
* Copyright (C) 2014 Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
*
diff --git a/src/udev/udevadm.c b/src/udev/udevadm.c
index b86d8921f3..af1b5a9186 100644
--- a/src/udev/udevadm.c
+++ b/src/udev/udevadm.c
@@ -1,4 +1,5 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/*
* Copyright (C) 2007-2012 Kay Sievers <kay@vrfy.org>
*
@@ -16,12 +17,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <stdio.h>
-#include <stddef.h>
#include <errno.h>
#include <getopt.h>
+#include <stddef.h>
+#include <stdio.h>
#include "selinux-util.h"
+#include "string-util.h"
#include "udev.h"
static int adm_version(struct udev *udev, int argc, char *argv[]) {
diff --git a/src/udev/udevd.c b/src/udev/udevd.c
index 20497ae8be..8627a81ec2 100644
--- a/src/udev/udevd.c
+++ b/src/udev/udevd.c
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/*
* Copyright (C) 2004-2012 Kay Sievers <kay@vrfy.org>
* Copyright (C) 2004 Chris Friesen <chris_friesen@sympatico.ca>
@@ -18,44 +20,53 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <stddef.h>
-#include <signal.h>
-#include <unistd.h>
#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
-#include <stdbool.h>
#include <string.h>
-#include <fcntl.h>
-#include <getopt.h>
+#include <sys/epoll.h>
#include <sys/file.h>
-#include <sys/time.h>
+#include <sys/inotify.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
#include <sys/prctl.h>
-#include <sys/socket.h>
#include <sys/signalfd.h>
-#include <sys/epoll.h>
-#include <sys/mount.h>
-#include <sys/wait.h>
+#include <sys/socket.h>
#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <sys/inotify.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <unistd.h>
#include "sd-daemon.h"
#include "sd-event.h"
-#include "terminal-util.h"
-#include "signal-util.h"
-#include "event-util.h"
-#include "netlink-util.h"
+#include "alloc-util.h"
#include "cgroup-util.h"
-#include "process-util.h"
+#include "cpu-set-util.h"
#include "dev-setup.h"
+#include "fd-util.h"
#include "fileio.h"
-#include "selinux-util.h"
-#include "udev.h"
-#include "udev-util.h"
#include "formats-util.h"
+#include "fs-util.h"
#include "hashmap.h"
+#include "io-util.h"
+#include "netlink-util.h"
+#include "parse-util.h"
+#include "proc-cmdline.h"
+#include "process-util.h"
+#include "selinux-util.h"
+#include "signal-util.h"
+#include "socket-util.h"
+#include "string-util.h"
+#include "terminal-util.h"
+#include "udev-util.h"
+#include "udev.h"
+#include "user-util.h"
static bool arg_debug = false;
static int arg_daemonize = false;
@@ -181,7 +192,7 @@ static void worker_free(struct worker *worker) {
assert(worker->manager);
- hashmap_remove(worker->manager->workers, UINT_TO_PTR(worker->pid));
+ hashmap_remove(worker->manager->workers, PID_TO_PTR(worker->pid));
udev_monitor_unref(worker->monitor);
event_free(worker->event);
@@ -224,7 +235,7 @@ static int worker_new(struct worker **ret, Manager *manager, struct udev_monitor
if (r < 0)
return r;
- r = hashmap_put(manager->workers, UINT_TO_PTR(pid), worker);
+ r = hashmap_put(manager->workers, PID_TO_PTR(pid), worker);
if (r < 0)
return r;
@@ -340,7 +351,7 @@ static void worker_spawn(Manager *manager, struct event *event) {
switch (pid) {
case 0: {
struct udev_device *dev = NULL;
- _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
int fd_monitor;
_cleanup_close_ int fd_signal = -1, fd_ep = -1;
struct epoll_event ep_signal = { .events = EPOLLIN };
@@ -881,7 +892,7 @@ static int on_worker(sd_event_source *s, int fd, uint32_t revents, void *userdat
}
/* lookup worker who sent the signal */
- worker = hashmap_get(manager->workers, UINT_TO_PTR(ucred->pid));
+ worker = hashmap_get(manager->workers, PID_TO_PTR(ucred->pid));
if (!worker) {
log_debug("worker ["PID_FMT"] returned, but is no longer tracked", ucred->pid);
continue;
@@ -1185,7 +1196,7 @@ static int on_sigchld(sd_event_source *s, const struct signalfd_siginfo *si, voi
if (pid <= 0)
break;
- worker = hashmap_get(manager->workers, UINT_TO_PTR(pid));
+ worker = hashmap_get(manager->workers, PID_TO_PTR(pid));
if (!worker) {
log_warning("worker ["PID_FMT"] is unknown, ignoring", pid);
continue;
@@ -1548,7 +1559,7 @@ static int manager_new(Manager **ret, int fd_ctrl, int fd_uevent, const char *cg
r = sd_event_default(&manager->event);
if (r < 0)
- return log_error_errno(errno, "could not allocate event loop: %m");
+ return log_error_errno(r, "could not allocate event loop: %m");
r = sd_event_add_signal(manager->event, NULL, SIGINT, on_sigterm, manager);
if (r < 0)
@@ -1641,7 +1652,8 @@ exit:
int main(int argc, char *argv[]) {
_cleanup_free_ char *cgroup = NULL;
- int r, fd_ctrl, fd_uevent;
+ _cleanup_close_ int fd_ctrl = -1, fd_uevent = -1;
+ int r;
log_set_target(LOG_TARGET_AUTO);
log_parse_environment();
diff --git a/src/udev/v4l_id/v4l_id.c b/src/udev/v4l_id/v4l_id.c
index 5c57db44c1..377eb7a72c 100644
--- a/src/udev/v4l_id/v4l_id.c
+++ b/src/udev/v4l_id/v4l_id.c
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/*
* Copyright (C) 2009 Kay Sievers <kay@vrfy.org>
* Copyright (c) 2009 Filippo Argiolas <filippo.argiolas@gmail.com>
@@ -13,19 +15,20 @@
* General Public License for more details:
*/
-#include <stdio.h>
-#include <errno.h>
-#include <string.h>
#include <ctype.h>
-#include <stdlib.h>
-#include <unistd.h>
+#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
-#include <sys/types.h>
-#include <sys/time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
#include <linux/videodev2.h>
+#include "fd-util.h"
#include "util.h"
int main(int argc, char *argv[]) {
diff --git a/src/update-done/update-done.c b/src/update-done/update-done.c
index 01bbde8455..4c44d50613 100644
--- a/src/update-done/update-done.c
+++ b/src/update-done/update-done.c
@@ -19,8 +19,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
+#include "fd-util.h"
+#include "io-util.h"
#include "selinux-util.h"
+#include "util.h"
#define MESSAGE \
"This file was created by systemd-update-done. Its only \n" \
diff --git a/src/update-utmp/update-utmp.c b/src/update-utmp/update-utmp.c
index b2998dce43..3883b33407 100644
--- a/src/update-utmp/update-utmp.c
+++ b/src/update-utmp/update-utmp.c
@@ -29,15 +29,16 @@
#include "sd-bus.h"
+#include "alloc-util.h"
+#include "bus-error.h"
+#include "bus-util.h"
+#include "formats-util.h"
#include "log.h"
#include "macro.h"
-#include "util.h"
#include "special.h"
-#include "utmp-wtmp.h"
-#include "bus-util.h"
-#include "bus-error.h"
#include "unit-name.h"
-#include "formats-util.h"
+#include "util.h"
+#include "utmp-wtmp.h"
typedef struct Context {
sd_bus *bus;
@@ -47,7 +48,7 @@ typedef struct Context {
} Context;
static usec_t get_startup_time(Context *c) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
usec_t t = 0;
int r;
@@ -62,7 +63,7 @@ static usec_t get_startup_time(Context *c) {
&error,
't', &t);
if (r < 0) {
- log_error("Failed to get timestamp: %s", bus_error_message(&error, -r));
+ log_error_errno(r, "Failed to get timestamp: %s", bus_error_message(&error, r));
return 0;
}
@@ -84,7 +85,7 @@ static int get_current_runlevel(Context *c) {
{ '1', SPECIAL_RESCUE_TARGET },
};
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
unsigned i;
@@ -105,10 +106,8 @@ static int get_current_runlevel(Context *c) {
"ActiveState",
&error,
&state);
- if (r < 0) {
- log_warning("Failed to get state: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_warning_errno(r, "Failed to get state: %s", bus_error_message(&error, r));
if (streq(state, "active") || streq(state, "reloading"))
return table[i].runlevel;
@@ -130,8 +129,7 @@ static int on_reboot(Context *c) {
if (c->audit_fd >= 0)
if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_BOOT, "", "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 &&
errno != EPERM) {
- r = log_error_errno(errno,
- "Failed to send audit message: %m");
+ r = log_error_errno(errno, "Failed to send audit message: %m");
}
#endif
@@ -160,8 +158,7 @@ static int on_shutdown(Context *c) {
if (c->audit_fd >= 0)
if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_SHUTDOWN, "", "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 &&
errno != EPERM) {
- r = log_error_errno(errno,
- "Failed to send audit message: %m");
+ r = log_error_errno(errno, "Failed to send audit message: %m");
}
#endif
@@ -211,8 +208,7 @@ static int on_runlevel(Context *c) {
return log_oom();
if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_RUNLEVEL, s, "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 && errno != EPERM)
- r = log_error_errno(errno,
- "Failed to send audit message: %m");
+ r = log_error_errno(errno, "Failed to send audit message: %m");
}
#endif
@@ -256,7 +252,7 @@ int main(int argc, char *argv[]) {
if (c.audit_fd < 0 && errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT)
log_error_errno(errno, "Failed to connect to audit log: %m");
#endif
- r = bus_open_system_systemd(&c.bus);
+ r = bus_connect_system_systemd(&c.bus);
if (r < 0) {
log_error_errno(r, "Failed to get D-Bus connection: %m");
r = -EIO;
@@ -284,6 +280,6 @@ finish:
audit_close(c.audit_fd);
#endif
- sd_bus_unref(c.bus);
+ sd_bus_flush_close_unref(c.bus);
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/user-sessions/user-sessions.c b/src/user-sessions/user-sessions.c
index 7c736c44d2..d28b196c4e 100644
--- a/src/user-sessions/user-sessions.c
+++ b/src/user-sessions/user-sessions.c
@@ -19,12 +19,15 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
#include <errno.h>
+#include <unistd.h>
+#include "fileio.h"
+#include "fileio-label.h"
#include "log.h"
+#include "selinux-util.h"
+#include "string-util.h"
#include "util.h"
-#include "fileio.h"
int main(int argc, char*argv[]) {
@@ -39,6 +42,8 @@ int main(int argc, char*argv[]) {
umask(0022);
+ mac_selinux_init(NULL);
+
if (streq(argv[1], "start")) {
int r = 0;
@@ -64,7 +69,7 @@ int main(int argc, char*argv[]) {
} else if (streq(argv[1], "stop")) {
int r;
- r = write_string_file("/run/nologin", "System is going down.", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
+ r = write_string_file_atomic_label("/run/nologin", "System is going down.");
if (r < 0) {
log_error_errno(r, "Failed to create /run/nologin: %m");
return EXIT_FAILURE;
@@ -75,5 +80,7 @@ int main(int argc, char*argv[]) {
return EXIT_FAILURE;
}
+ mac_selinux_finish();
+
return EXIT_SUCCESS;
}
diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c
index 6353579283..622fbe9a6d 100644
--- a/src/vconsole/vconsole-setup.c
+++ b/src/vconsole/vconsole-setup.c
@@ -19,25 +19,31 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
-#include <stdbool.h>
#include <limits.h>
-#include <sys/ioctl.h>
-#include <linux/tiocl.h>
#include <linux/kd.h>
+#include <linux/tiocl.h>
#include <linux/vt.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
-#include "util.h"
-#include "log.h"
-#include "virt.h"
+#include "alloc-util.h"
+#include "fd-util.h"
#include "fileio.h"
+#include "io-util.h"
+#include "locale-util.h"
+#include "log.h"
#include "process-util.h"
-#include "terminal-util.h"
#include "signal-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
+#include "terminal-util.h"
+#include "util.h"
+#include "virt.h"
static bool is_vconsole(int fd) {
unsigned char data[1];
@@ -210,11 +216,11 @@ static void font_copy_to_all_vcs(int fd) {
continue;
/* skip non-allocated ttys */
- snprintf(vcname, sizeof(vcname), "/dev/vcs%i", i);
+ xsprintf(vcname, "/dev/vcs%i", i);
if (access(vcname, F_OK) < 0)
continue;
- snprintf(vcname, sizeof(vcname), "/dev/tty%i", i);
+ xsprintf(vcname, "/dev/tty%i", i);
vcfd = open_terminal(vcname, O_RDWR|O_CLOEXEC);
if (vcfd < 0)
continue;
@@ -270,7 +276,7 @@ int main(int argc, char **argv) {
fd = open_terminal(vc, O_RDWR|O_CLOEXEC);
if (fd < 0) {
- log_error_errno(errno, "Failed to open %s: %m", vc);
+ log_error_errno(fd, "Failed to open %s: %m", vc);
return EXIT_FAILURE;
}